Your website needs a CSP. Here’s why

Your website needs a CSP. Here’s why

Here’s a scenario:

  1. You create a website and make it available online.
  2. Your website ends up getting hacked (it happens frequently, by the way..)
  3. The nefarious party is able to inject some malicious javascript into one of your pages.
  4. A legitimate user visits your website and is redirected to a phishing page as a result of the work done by an attacker.
  5. The user attempts to log into the page thinking it’s your website and ends up getting all of his credentials stolen.

This is not good – nobody wants their website to be hacked and getting their credentials stolen – but it happens. In fact, such events are unfolding constantly, and websites getting breached left, right and centre were the force behind the idea for me to establish BreachDirectory.

Such events can unfold differently too. I’ll start from the third stage:

  1. The nefarious party is able to inject some malicious javascript into one of your pages.
  2. A legitimate user visits your website and browses it as usual.

See any differences here? The user did not get his credentials stolen because the malicious payload did not execute – it violated the CSP that was present on your website.

CSP? What CSP?

CSP stands for Content Security Policy. The primary goal of using a Content Security Policy on your website is to mitigate certain types of attacks, including Content Injection and Cross-Site Scripting (XSS).

Content Security Policy can be enforced by sending an HTTP response header. The policy itself can contain one or more CSP directives – these are the “rules” that your browser must abide by.

CSP Directives

There are more than 20 CSP directives that can be used. I’ll mention a few of them:

  • script-src – defines valid sources that the browser can load JavaScript from
  • style-src – defines valid sources for stylesheets
  • img-src – defines valid sources for images
  • require-sri-for – allows website developers to ensure that Subresource Integrity is used for scripts and / or styles
  • upgrade-insecure-requests – instructs the user agent to upgrade all HTTP requests to HTTPS before fetching them
  • report-uri – instructs the user agent to report Content Security Policy violations. (Will be deprecated in CSP 3. See report-to)
  • report-to – allows developers to define the reporting group that reports should be sent to.

Building the policy

A basic Content Security Policy might look something like this:

Content-Security-Policy: default-src ‘self’

If one of your pages contains this HTTP response header, the browser would only be allowed to load content from the same website as the page returning the header. This means that you wouldn’t be able to load content from third-party Content Delivery Networks such as CloudFlare or Bootstrap. If you would want to load scripts from them you could enforce the following header:

Content-Security-Policy: default-src ‘self’; script-src ‘self’ cdnjs.cloudflare.com

This header will tell the browser that by default, it can load any type of content from your domain, but scripts can only be loaded from cdnjs.cloudflare.com. If you load a script that isn’t from there while the above HTTP header is in place, the browser would refuse to load it:

What about this code block? It isn’t malicious, so it should be allowed to run, right?

<script>
$(“#button”).click(function() {
alert(“You clicked a button.”);
});
</script>

Wrong – that’s blocked too:

There are a few ways a website developer could get the above code block to run:

  1. An “unsafe-inline” keyword needs to be specified in the header
  2. The entire code block can be white-listed by specifying a “nonce” (a number, that can be only used once)
  3. A developer can also specify a hash.

The “unsafe-inline” keyword allows any script to run – if your website would get hacked and the attacker would want to redirect users to a malicious website by injecting some javascript into one of your pages, he could do that. This isn’t very good so lets have a look at the second option, now we will use a slightly different policy – we will add a nonce:

Content-Security-Policy: default-src ‘self’; script-src cdnjs.cloudflare.com ‘nonce-ZDE0OTIyMzcwMjNiZTYyMjY2MTUzZjQ3NmRlYTczYzU=’

A nonce allows you to white-list an entire code block:

<script nonce=”ZDE0OTIyMzcwMjNiZTYyMjY2MTUzZjQ3NmRlYTczYzU=”>
$(“#button”).click(function() {
alert(“You clicked a button.”);
});
</script>

Note that the nonce in the header is exactly the same as the nonce in the script tag: it has to be the same, otherwise our script won’t execute. But hey, there’s also a third option, so we should have a look at that too – the browser told us that in order for the script to execute, the following policy can be used:

Content-Security-Policy: default-src ‘self’; script-src cdnjs.cloudflare.com ‘sha256-UEDzDzSiQoZnNVNOKXXr37XeW1TYAb7S7jtVORuIUTk=’

This allows developers to run the above code block, but if the contents of the code block would change, the hash would be different too – the browser would refuse to load the script.

Other uses of CSP

Content Security Policy can also be used to ensure that Subresource Integrity (SRI) is used across all of the styles and scripts in your website – there’s a header that might help you achieve this:

Content-Security-Policy: require-sri-for script style;

That’s it! It’s that simple.

When using CSP, a browser can be forced to upgrade any HTTP request to HTTPS before issuing it (to accomplish this, you can use upgrade-insecure-requests) or you could block all mixed content entirely by using a directive called block-all-mixed-content – whatever floats your boat.

After you’ve deployed Content Security Policy to your website, it would be a good idea to check if you’ve done so correctly: Scott Helme’s tool called SecurityHeaders should help you accomplish that.

Logging violations

There’s little use of a CSP if you don’t record what was violated. You need to tell the policy where to send the reports too – use the “report-uri” directive:

Content-Security-Policy: default-src ‘self’; script-src ‘self’ cdnjs.cloudflare.com; report-uri https://domain.com/reporturi.php

This will instruct the user agent to report attempts to violate the Content Security Policy and send them to a specified URI. The directive is deprecated though – since CSP 3, “report-to” should be used instead.

Conclusions

  • Content Security Policy (CSP) is important – it prevents multiple types of attacks by allowing a developer to specify the locations where the browser can load resources from via an HTTP header.
  • There are a lot of CSP directives that can be used – I have only mentioned a few of them.
  • Using CSP? Enable reporting – it’s essential.

Before enforcing the policy, make sure to test it in a “report-only” mode for some time though – this way a developer could see the violation reports but nothing would break on their website. An HTTP response header called “Content-Security-Policy-Report-Only” makes this possible by monitoring the effects of the policy without applying them.