If you are unfamiliar with Content Security Policies (CSPs), then you can find out more info on the Mozilla site.
A Content Security Policy is probably the best defence that you can introduce to your web services. They provide a high return on time invested as they are straightforward to implement when you know what you are doing and provide protection against a wide variety of attacks. A poorly-configured CSP is likely to provide at least some protection but obviously a properly-configured CSP is much more effective at securing your site.
We are going to discuss a few common mistakes that people make when adding a CSP to their site and how to avoid them.
Issue: Leaving your CSP in report-only mode
Report-only mode is really useful for introducing a content security policy to an existing site but it should only be used for a short amount of time to ensure no parts of your site are broken by your CSP. In report-only mode, your CSP will merely report violations. It will not enforce your configured rules and so it offers little to nothing in the way of security.
It should be clearly planned beforehand how long your CSP will be left in this mode so it doesn’t get forgotten. When you are ready to turn off ‘report-only’ mode, it’s essential that your CSP is checked in your development environments with ‘report-only’ turned off so that there are no issues when it gets to your production environment.
Issue: Allowing Unsafe-inline Scripts Without Nonces or Hashes
Allowing ‘Unsafe-inline’ for script sources on its own is a great way to reduce the effectiveness of your CSP. Many sites add this directive to stop it breaking their site but adding the directive removes one of the key defences from your CSP which is that it only allows scripts that you have whitelisted to run.
It is called ‘unsafe-inline’ for a reason! Why add a CSP if you’re not going to allow it to do anything?
If you specify ‘unsafe-inline’ on its own then your CSP will not block any scripts that an attacker manages to inject into your site.
There are 3 options if you currently have inline scripts:
- Move each script into a separate file. If you have the ‘self’ directive, the file can then just be referenced using a relative link.
- Add a hash of each script to your CSP. A hash is generated for an inline script which is then added to the script-src directive of your CSP. If an attacker manages to inject a script into your web page then it will not match the hash of your script and so only your script will be allowed to run. This option can be time-consuming if there are a lot of inline scripts and you would need to regenerate a hash if you edit a script.
- Add a nonce to your scripts. (This can be done easily with NWebSec Tag Helpers) This will generate a nonce for each request that will be set in the CSP header and on the script tag. This means that if an attacker managed to inject a script into your web page, even if they added a nonce, it wouldn’t match a nonce in the CSP header whitelist and so it wouldn’t be allowed to run.
The same issues and options also apply with regards to inline styles.
Issue: Not Specifying Rules For All Directives
If no rules are specified for a particular directive then all sources are allowed. I.e. If you don’t specify ‘font-src’ then it’s the equivalent of adding the directive ‘font-src: *’. This means it is important to be aware of all the directives that can be specified so that you can restrict them appropriately.
The ‘default-src’ directive allows you to specify the default. In general, the ‘default-src’ can be used as a fallback for all directives that have ‘-src’ at the end (complete list of directives that ‘default-src’ applies to). ‘Default-src’ won’t be applied to the following directives:
These directives need to be explicitly set, if they aren’t, there are various attacks that can exploit them; the lack of a ‘base-uri’ directive is exploited in base tag hijacking attacks. If your site doesn’t make use of a resource type then you should still add a directive for it with ‘none’ specified.
Issue: Using Data Urls
An issue sometimes encountered when creating CSPs is that often assets such as fonts are loaded using a data url and you get an error like the one below.
The simple solution, as you can read on this stack overflow article, is of course to update the ‘font-src’ directive on your CSP to: ‘font-src: self https: *.gstatic.com data:’
WRONG! Turns out Stack Overflow doesn’t always have the correct answer! Whilst adding the ‘data:’ directive to your whitelist of allowed sources will stop them being blocked, you will also be creating a large security hole within your CSP’s defences as it will now allow any font to be loaded using any data URL.
Probably the simplest solution is just to not use data URLs at all but if you need to use them for some reason then you should specify the exact data URL rather than allowing any URLs that match the pattern ‘data:’
There is no silver bullet when it comes to web security but implementing a CSP correctly is a useful way to greatly reduce the attack surface for malicious visitors. There are many security flaws that a CSP won’t help to prevent such as logic-based authentication flaws and that’s why it’s important to be able to trust a system’s architects and developers to create it securely.
At Razor, we create systems that are secure by design and use open source projects such as NWebSec to keep up to speed with the ever-changing security requirements. We then implement the appropriate checks to continually monitor them.
If you need any help or just want to check that you have the right security in place, not just security headers but across your entire application and infrastructure, get in touch, we can help.