Implementing Content Security Policy for your website

02 Feb 2019

Website security has become an increasingly hot topic in the last few years. Even with this increasing focus on security many websites still have easy to fix vulnerabilities. One of the vulnerabilities many, both small and large sites, are still susceptible to is Cross Site Scripting (XSS). According to OWASP, XSS issues can be found in around two-thirds of all applications (see OWASP top 10-2017, A7:2017). Using modern frameworks in development and proper secure development methods and guidelines can go a long way towards eliminating XSS vulnerabilities, but for many legacy applications this will be difficult if not impossible. Implementing a Content Security Policy is a relatively easy way to prevent many forms of XSS vulnerabilities for both legacy and modern websites. For legacy applications Content Security Policy can be the only feasible XSS prevention technique, for more modern applications it can act as a additional line of defense complementing the prevention techniques already implemented in the application.

What is Content Security Policy

Content Security Policy (often abbreviated to CSP) is basically a set of rules (policies) defining from which sources certain resources are allowed to be used. You set those rules by including the CSP response header to your websites responses. In the header you set the rules which make up your security policy. When a browser supporting CSP gets a response from your website which includes the CSP header the browser will ensure that the rules you set are enforced.

A simple example of a CSP rule would be : scripts resources may only be included if they are from my own domain or from google.com

CSP allows defining rules for a number of resources:

  • scripts
  • stylesheets
  • images
  • objects
  • media (video and audio)
  • fonts
  • iframes
  • workers

The rules for each of these types of resources are defined in so called directives. For each of these directives you can add one ore more whitelisted domains. Using a proper whitelist you can prevent resources (most notably javascript files) from being included from unsafe domains. This can prevent or decrease the risk of an attacker injecting unsafe resources in your website. Apart from whitelisting domains there are a few additional rules. The most important of those rules controls if executing inline Javascript or CSS is allowed. By default inline Javascript and CSS is disallowed, to allow it you have to explicitly allow it. Disabling inline Javascript prevents many forms of XSS attacks, so if it is possible for your application keep it disabled. If you have to allow inline Javascript you also have the option to only allow specific inline scripts, those specific scripts can be identified by adding a hash or a nonce (number used once).

This blog is not intended to discuss all CSP directives and settings in detail, for those details a good site is the Mozilla Developers Network.

Examples

Example 1

The following example is the most basic implementation. The value 'self' means only the same domain (as the website which set the header) is allowed. The default-src directive is used to define a default policy and is used as fallback for all directives which are not explicitly set.

Content-Security-Policy: default-src 'self';

Example 2

This example allows images to be included from all domains, media (video and audio) only from media1.com and media2.com and scripts only from the same domain as your site and from the domain userscripts.example.com. All other sources (like stylesheets and fonts) are not explicitly set, so they fallback to the default-src directive which is set to 'self'.

Content-Security-Policy: default-src 'self'; img-src *; media-src media1.com media2.com; script-src 'self' userscripts.example.com

Example 3

The last example again allows only media from the domains media1.com and media2.com. Images are only allowed from site using HTTPS. Scripts are set to be allowed from the same domain and inline scripts are explicitly allowed by setting 'unsafe-inline'.

Content-Security-Policy: default-src 'self'; img-src https: ; media-src media1.com media2.com; script-src 'self' 'unsafe-inline'

Points of concern

Adding a CSP to your website is not a magical solution to your XSS (and some other) security vulnerabilities. There are some things you have to take into account before you begin implementing it.

Browser support

Not all browsers support CSP, most notably Internet Explorer. And some browsers have limited CSP support. So if you are unlucky enough to have a lot of users visiting your website with older browsers, look very carefully which CSP directives you can actual implement. See Can I Use CSP for the latest browser support.

There are multiple versions of CSP

The current version (at the time of writing) is CSP 2.0, but older browsers only support version 1.0. The upcoming 3.0 version will have some possible breaking changes. So look carefully at what CSP versions your clients will support. Some of the bigger changes between CSP 2.0 and 3.0 are:

  • frame-src directive which was deprecated in CSP 2.0 is un-deprecated in CSP 3.0
  • report-uri directive has been renamed to report-to

See W3C documentation CSP 3 for all changes from version 2.0 to 3.0. And W3C documentation CSP 2 for changes from level 1.0 to 2.0.

Users can switch CSP off in their browser

Because CSP is a security feature implemented by browsers, you have no actual control if users actual use it. With browser extensions a user could disable CSP in their browser. If they do, it is basically at their own risk but just keep in mind that the fact that all your users have browsers which support CSP does not mean that those users are actually protected by their browsers CSP feature.

Implementation steps

Implementing a proper CSP for your site is not trivial, especially for larger sites. The followings steps will help smoothen your implementation.

1. Find out what resources your website uses

Before you can create a policy to whitelist certain resources you will first have to determine which resources your website is actually using. There are a couple of approaches.

  • Open the most important pages in your site in a browser and use your browsers developer tools to inspect all resources used by the page you are viewing.
  • Dive into the source code of your website and try to determine which resources are included
  • Ask your most experienced developers to get you a list of what resources they can think of

Pay extra attention to any analytics tools your site uses, some of those tools inject javascript in your site which does it's own calls to external resources. Those resources won't be found in your source code. Using one or more of those approaches will give you a list of all resources you need to include in your initial policy to ensure your website won't stop working when you implement the policy.

2. Create an initial policy

Once you have a decent list of resources, you can start creating your first CSP rules by extracting all the unique domains from your list of resources and mapping them to the CSP directives (like script-src, style-src and so on).

3. Document your policy rules

The third step is meant to make sure you and your organisation can see which CSP rules are in place, why they are necessary and when and by who they are approved. Your documentation could for example hold a list of allowed domains, a description of what they are used for and a date of when they are approved.

4. Release your policy to production in report-only mode

Once your initial CSP rules are defined and documented you can implement them in your website in report-only mode. In report-only mode every time your production website violates any CSP rule a report is send to a report url you supply. You do this by using the header Content-Security-Policy-Report-Only in stead of Content-Security-Policy and adding the report-uri directive with a valid url which can receive the violation reports.

Content-Security-Policy-Report-Only: default-src 'self'; img-src *; media-src media1.com media2.com; script-src userscripts.example.com report-uri http://your-report-uri.com

You can host your own application for receiving the violation reports but an easy alternative is using a commercial CSP report handling solution like Report Uri.

5. Review the policy violation reports

Once your CSP violations are properly collected you wil have to start analysing them. Allow some time to collect enough reports before analysing. Browsing through all violations will most likely reveal some unexpected calls from your website to external resources. Check out if those calls are necessary and if so document them (see step 3).

6. Tweak your policy

With the result of your violation report review (step 5) you can update your CSP rules, allowing extra domains where necessary.

7. Repeat steps 4 to 6

Depending on how many violations are found in step 5 you can do another review cycle (steps 4 - 6).

8. Introduce and enforce procedures for updating your policy

You now have a proper set of CSP rules and a documented list of why those rules are necessary. Now you have to make sure that any future change to your rules is done according to a clear procedure. Not just any developer who needs to use an external resource should be able to update the CSP rules. A way of working could be to only allow changes to the actual CSP rules when those changes are listed and approved in your documented list (see step 3).

9. Introduce and enforce scheduled manual validation actions

To make sure your documented list keeps representing the actual CSP implementation on your website it is advisable to introduce scheduled checks. You could introduce a quarterly validation actions for development teams to validate if all rules in the documented list are properly implemented. If you do so, make sure you document all validation actions with their date and results. This will supply proof to your organisation that the CSP rules are really kept up to date.

10. Implement automated validations

As an extra validation you can add automated validations. A good option would be to run an CSP validation tool during your build and deploy process. A few available tools at the moment of writing are:

11. Release your policy in enforce mode

At this point you are finally ready to release your CSP to production in enforce mode, this means you remove the report-only from your CSP header and your rules will be enforced from now on. You can still leave the report-uri directive in your CSP header if you wish. All violations of your CSP rules will still be reported in that case. If you do a future update to your CSP you can also have both a report-only and an enforce CSP header at the same time. They can even both report violations to different uri's.

Content-Security-Policy: default-src 'self'; script-src userscripts.example.com report-uri http://your-current-report-uri.com

Content-Security-Policy-Report-Only: default-src 'self'; img-src *; media-src media1.com media2.com; script-src userscripts.example.com report-uri http://your-new-report-uri.com

12. Keep monitoring violations of your policy

As mentioned in the previous step you can keep reporting all CSP violations even for an enforced CSP. If you choose to do this, make sure that you keep monitoring your violation reports periodically. You could include an analysis of the violation reports in the periodical validation actions (step 9).

Conclusion

By adding proper CSP rules to your website you can reduce a great number of possible security vulnerabilities. Al lot of XSS vulnerabilities can be mitigated this way. Implementation can be difficult for larger websites but by following the suggested steps your implementation will be made easier, safer and more controlled. As an added bonus you will have the right controls in place to keep your CSP in sync with the (security) requirements of your organisation.