Increase security by enforcing multiple Content Security Policies

In my article "I don't trust your browser" I wrote about using CSP as a <meta> - tag and in the response headers that should be used as some sort of "fallback". This article will go more into depth regarding how that works and scenarios where it can protect the user against certain attacks.


How it works

Let's assume that you have this CSP in your HTML:
<meta http-equiv="Content-Security-Policy" content="img-src i1.sndcdn.com i.imgur.com">.

And you also have this CSP in the HTTP-headers:
Content-Security-Policy: img-src i.imgur.com

If you try to load an image from i1.sndcdn.com, do you think the resource will be blocked? No should be the obvious answer, because i1.sndcdn.com is whitelisted in the valid CSP and the meta-tag is rendered after the HTTP-header (data comes after the HTTP-header). But in fact, that's the not case - the image will be blocked. PoC

Why is this? It's simply the way CSP is implemented into browsers. The specification mentions this in ยง 3.5. Enforcing multiple policies. The short answer is that multiple policies will be applied and followed by the browser.

Of course should you have the exact same policy in your HTTP-header as you have in the <meta> - tag. I will shortly describe why.

Examples

The first example is if we posit that an attacker actually can remove and/or change the CSP in our <head> (CSP is not valid outside of <head>), the CSP delivered via HTTP-header will still be applied and the attack will not be successful. At all. The same goes for the headers. However, if an attacker can change stuff in the HTTP-header you have much worse issues.

The second example concerns active side attacks from extensions, MITM and malware. It's been proven that malware have been removing the CSP-protection in order to be able to inject scripts that read valuable information such as user credentials and credit card information. The malware can however just remove the CSP in both <meta> and in the headers to remove the protection completely.

The image above shows the extension "Disable Content-Security-Policy" in action. However, due to CSP in a <meta> - tag, the resource was still blocked even though the CSP delivered in the response headers was removed by the extension.

A third example could be cache attacks. If a proxy caches the headers or the document, an attacker may be able to get the proxy to cache a forged CSP that is delivered to clients using the proxy. (There will most definitely be an article about cache-related attacks in the future because they are really interesting.)

Caveats

There are still some things you should know about this method before you deploy it.

Most importantly is that you should have the same policy in your headers as in you HTML. However, some directives (frame-ancestors, sandbox and report-uri) are not allowed in a <meta> - tag. One reason behind this, is the danger these directives or its attributes have if an attacker can change them.

Using a CSP as a <meta> - tag is more dangerous than using it in the headers due to a few reasons. The first reason is that the chance of an attacker having the possibility to change the CSP in <meta> is bigger than having the possibility to change the one in the header. The second reason is that a <meta> - tag can only be applied on an HTML source while headers can be sent on all requests (It's not possible to directly insert CSP into DOM).

One problem here is that an attacker can add directives to CSP in the <meta> - tag to whitelist resources if the directive does not exist in the header. PoC - Therefore it's very important that you have a complete policy with all the directives with values set in both your <meta> - tag and in the HTTP-headers.

Unfortunately it's possible to use multiple <meta> - tags for defining a CSP. If we have multiple policies in our HTML, they will be applied vertically starting from the top to the bottom of the document.

Imagine the following:

<meta http-equiv="Content-Security-Policy" content="img-src i.imgur.com">

<!-- The below CSP was injected by the attacker -->  
<meta http-equiv="Content-Security-Policy" content="img-src evil.com">  
<meta http-equiv="Content-Security-Policy" content="script-src evil.com">

Since the attacker managed to insert two meta-tags below the "original", the final policy will simply be:

img-src i.imgur.com; script-src evil.com  

The solution to this is simply to specify all directives, otherwise there will be a possibility for the attacker to specify one in an inserted policy. This solution works because the attacker won't be able to fully control a directive that has been specified in the original policy.

However, the attacker can specify already specified directives with another attribute (just like the above example) if the server didn't send a CSP in the response header. Therefore it's of much importance that you have a policy in both your response headers and in your HTML.


Summary and conclusion

Don't like reading? No problem, here's the summary:

  • Use CSP both in a <meta> - tag and in your response headers
  • Use the same policy - do not mix policies
  • Specify all directives and give them valid attributes
  • Have the <meta> - tag right after <head>

Personally, I would like to see the browser only allowing one (the first) policy and ignoring any policies specified after. This would force the attacker to write before the actual policy or change its directives and attributes, which is much more unlikely than being able to insert data in the <head> section.

We have a form of "race condition" problem because of this, which actually is not needed. Worth to mention is that the same goes for the response headers - multiple Content-Security-Policy headers will be applied just like using multiple policies in HTML.




I hope you enjoyed this reading and if you have any questions you can reach me on Twitter (@dotchloe) or simply send an email to chloe@chloe.re