Hardened Content-Security-Policy - Part 2

This is the second blog post in the series Hardened Content-Security-Policy where I write about some tips for securing Content-Security-Policy (CSP). Part 1 can be found here.

Static resources should have strict CSP

Static resources such as images, Javascript, stylesheets, flash and SVG should have a strict CSP like
default 'none'; referrer no-referrer; upgrade-insecure-requests. This policy will not be applied when you embed them, the policy will only be in use when you directly visit them, for example https://example.com/image.jpg

This could protect in some cases where users (or you) can upload just some type of files, but for some reason this protection gets bypassed and an attacker manages to upload "dangerous" files. Even if the worst case scenario happens where an attacker manages to upload an HTML-file, the attack would be very much mitigated due to the strict CSP. Example at Github

CSP in meta right after <head>

If you're using CSP in <meta> - tags you should have the tag right after <head>. This is because resources before the <meta> - tag is not blocked by CSP. This is simply due to the way browsers are rendering HTML, that is from the top to the bottom.

Here's a PoC.

If an attacker would have the possibility to inject HTML before the CSP in meta, the content would not be blocked by CSP.

SRI and require-sri-for

Even if you don't use external sites to host your stylesheets and Javascript files, you should use SRI. There's no reason not to ... security-wise that is. SRI is not a CSP-directive, but require-sri-for is.

By adding require-sri-for style script in your CSP, the browser will not load resources (.css & .js) that does not have a correct SRI hash specified.

This could, in some specific situations protect when an attacker only can control the src - attribute in a script or style link. It could also protect in situations where files on your server would serve an old library that's vulnerable, but the SRI hash was generated for the updated library. Or, clients that cache Javascript-files but the document (HTML) is updated with a new SRI because the file has been changed - in this case would the resource not be loaded because cached resources are checked against the current SRI-hash in the document.

Go full path, or no path at all

As explained in my previous article Bypassing paths in CSP with open redirects I show that it's possible to bypass paths in CSP with redirects. I've already explained some mitigation in that article but it's worth to mention once again.

So if you're using paths in your CSP, you should use paths even on your origin. This means that you should avoid 'self' and instead use your domain with specified paths to the resource.

This would protect if you're using paths but also 'self' because the chance would increase that the attacker find an open redirect on your domain and use that to bypass all the paths.

If you don't use paths, you can ignore this tip.

EDIT: Patrick Toomey had the idea of avoiding 'self' completely and instead use another origin for user controlled data. That's a great hardening tip!

Don't use report-uri

I've been working with many CSP reports in my short lifetime and can only draw the conclusion that it's very time consuming and the wrong approach.

The main problem with CSP reports is the verbosity. Browsers make a lot of noise with theirs extensions and just overall strange behavior. Another problem is that CSP reports can easily be spoofed and if you don't analyze the reports and find the root cause you may whitelist something that's all based on fake reports, which may introduce bypasses.

Instead, design your website better and have this design applied over everything. For example, you should really avoid unsafe-inline so even if you have a dynamic CSP or many subdomains with different polices, you should be able to use the same policy. Dynamic CSP is good, but the only thing that's better is a strict global policy - which only can be applied if you correctly design your website.

That's it for this time!