Secure your website with Content Security Policy

Content security policy shield So what is a content security policy (CSP), and why do I need one? A CSP is a contract that your server sends to the browser, defining from which domains it's ok to load scripts, style sheets, images etc.

This is an important tool to protect against cross-site scripting (XSS), clickjacking and other client side attack vectors. XSS can for example be used by evildoers to place a script into your website, replace the login field in your online bank, and send usernames and passwords to somebody else. Another trick could be to load your page in an iframe on a similar domain, so it looks like your page loads normally, all the while evil hackers are snatching up passwords and credit card information.

These techniques can be virtually undetectable to the user, as everything will look normal, and since these attacks happens on the client side, it can be difficult to detect until the damage is done.

That's why you need a Content Security Policy!

Headers

To protect your website with a CSP, you only have to add a single line to your server configuration. If you already know about CSP, you can use my CSP config generator to create a configuration for your webserver.

If you are running Apache, you just need to add this single line to your .htaccess configuration file:

Header set Content-Security-Policy "default-src 'self'"

This line will configure your website to only load scripts, images etc. from the same domain. This is a little restrictive though, especially if you are running scripts from third parties like Google Analytics and CloudFlare. In that case your config should probably look more like this (line breaks added for readability):

Header set Content-Security-Policy "
    default-src 'self';
    script-src 'self' www.google-analytics.com *.cloudflare.com;
    img-src *.cloudflare.com
"

You can set a policy for most types of resources: scripts, images, style sheets, fonts etc. If you don't specifically define a header like script-src for scripts, the website will fallback to default-src.

Supported directives

  • default-src: Define loading policy for all resources type in case of a resource type dedicated directive is not defined (fallback)
  • script-src: Define which scripts the protected resource can execute
  • object-src: Define from where the protected resource can load plugins
  • style-src: Define which styles (CSS) the user applies to the protected resource
  • img-src: Define from where the protected resource can load images
  • media-src: Define from where the protected resource can load video and audio
  • frame-src: Define from where the protected resource can embed frames
  • font-src: Define from where the protected resource can load fonts
  • connect-src: Define which URIs the protected resource can load using script interfaces
  • report-uri: Specifies a URI to which the user agent sends reports about policy violation
  • sandbox: Specifies an HTML sandbox policy that the user agent applies to the protected resource

New in CSP2

CSP version 2 introduces some new directives. You should check out browser support first though, as it's kind of spotty right now.

  • form-action: Define which URIs can be used as the action of HTML form elements
  • frame-ancestors: Indicates whether the user agent should allow embedding the resource using a frame, iframe, object, embed or applet element, or equivalent functionality in non-HTML resources (replaces frame-src)
  • plugin-types: Define the set of plugins that can be invoked by the protected resource by limiting the types of resources that can be embedded
  • base-uri: Restricts the URLs that can be used to specify the document base URL
  • child-src: Governs the creation of nested browsing contexts as well as Worker execution contexts

Keywords

Each directive accepts domain patterns seperated by space, and domain patterns can contain both protocol and ports if you want to be specific: http://*.mysite.com:8080. Besides domain patterns there are some special keywords you can use:

  • 'none': Nothing is allowed.
  • 'self': Allow resources to load from the websites own (origin) domain.
  • 'unsafe-inline': Allows inline <script> and <style> elements. This can be further secured by specifying a hash of the code.
  • 'unsafe-eval': Allows use of eval() in scripts. Needed for Angular.
  • https: Forces all resources to be loaded over HTTPS.
  • data: Allows resources to be inlined with base64.

Additional important headers

There are a few extra headers worth setting while you're at it:

Strict-Transport-Security

Ensures that all traffic is sent through HTTPS.

Header set Strict-Transport-Security "max-age=631138519; includeSubDomains"

X-Frame-Options

Disallow your page to be embedded within a <frame>, <iframe> or <object>.

Header set X-Frame-Options DENY

X-Content-Type-Options

Disable MIME type sniffing, which can e.g. make IE execute an innocent looking .img URL as a javascript.

Header set X-Content-Type-Options nosniff

Apache, nginx and IIS example

Setting these headers is very easy, and following is an example configuration for each of the major webservers. If you prefer, I have also created a CSP generator tool, which allows you to create a CSP for all the mentioned webservers, as well as load an existing configuration file to edit.

Apache

<IfModule mod_headers.c>
    Header set Content-Security-Policy "default-src 'self'; img-src *.cloudflare.com; script-src 'self' www.google-analytics.com *.cloudflare.com"
    Header set X-Content-Type-Options nosniff
    Header set X-Frame-Options DENY
</IfModule>

nginx

add_header Content-Security-Policy "default-src 'self'; img-src *.cloudflare.com; script-src 'self' www.google-analytics.com *.cloudflare.com"
add_header X-Content-Type-Options nosniff
add_header X-Frame-Options DENY

IIS

<configuration>
   <system.webServer>
      <httpProtocol>
         <customHeaders>
            <add name="Content-Security-Policy" value="default-src 'self'; script-src 'self' www.google-analytics.com *.cloudflare.com; img-src *.cloudflare.com" />
            <add name="X-Content-Type-Options" value="nosniff" />
            <add name="X-Frame-Options" value="DENY" />
         </customHeaders>
      </httpProtocol>
   </system.webServer>
</configuration>