Web Security Training

Navigating the web security landscape

Navigating the web security landscape


A Step-by-Step Guide towards Deploying CSP

If you've been reading this blog, you have already heard about Content Security Policy (CSP), a really powerful and important browser security policy, introduced a few years ago. However, if you have taken a stab at implementing a CSP policy for your application, you may have noticed that there are many hurdles to overcome, you may have sworn profusely, and you may even have kicked CSP out the door altogether. I totally get it, CSP is a really complex beast, and retrofitting CSP to an existing web application can be extremely painful. However, I'm asking you to give CSP another chance, and keep reading to discover how to deploy CSP step by step, and which tools you can use to ease the process.

This is the first post in a series about Content Security Policy (CSP). The exact contents of the series is still in motion, but here are links to the posts that have already been published:

As I am currently working on the other posts. But I can already lift the veil on the treasure trove of information that will still be coming your way:

  • Coverage of CSP’s reporting feature
  • Awesome tools and services that do a lot of the work for you
  • How to deal with third-party components, such as a Twitter widget
  • How CSP interacts with Browser Extensions
  • Useful CSP recipes, that you can use to quickly build your own CSP policy

But every story has a beginning, so let’s start there. Whenever I talk about recent web security technologies, I try to make sure that I spend some time on Content Security Policy (CSP). The reason I do this is actually twofold. First, CSP is an extremely powerful browser security policy, giving you as a developer control over what happens in the browser context of your application. Second, ever since the first introduction of CSP as a research proposal, it has been gaining a lot of momentum, making it even the preferred mechanism to add new security features to the browser.

CSP is the security policy of the future, so you better start adding CSP support sooner rather than later.

What can CSP do for you?

“What can CSP do for me?” is probably the first question on your mind, closely followed by “is it even worth the effort?”. In case you’re wondering, the answer is yes!

The original goal of CSP was to put the developer back in control over what happens in the browser context of an application. For example, a typical web page includes a lot of resources (stylesheets, fonts, scripts, images, …) from all over the web. Most of the time, the developer intended to load these resources, but sometimes, an attacker manages to tell the browser to load a malicious resource. A very good illustration of such an unintended inclusion is a cross-site scripting (XSS) attack, as shown below. When the attacker injects such malicious code, the browser really has no clue that it should not load this code, and the developer has no way to warn the browser about this attack. The net result of such an attack is malicious code being executed in the application context, right there in the user’s browser.

In this schematic of a stored XSS attack, the attacker first submits legitimate application data with a hidden executable payload. The application embeds this legitimate data into a web page, unknowingly also embedding the injected code. When the user’s browser renders this page, the attacker’s code will be executed, allowing him to perform arbitrary actions in the application context.

CSP is intended as a defense-in-depth mechanism to reduce the harm of such injection vulnerabilities. Essentially, CSP locks your applicaton down, so that even if an injection occurs, the attack will be mitigated, or the attacker will be very restricted in his capabilities. To achieve this goal, CSP deploys two defense mechanisms.

First, CSP allows the developer to give the browser a list of resources that the page is allowed to load. Whenever the page wants to load a non-whitelisted resource, such as a maliciously injected script file, the browser will detect a policy violation, and prevent this from happening.

A second defense deployed by CSP is to block inline script and style blocks from being executed. This restriction is necessary, because there’s no way for the browser to tell if a script tag with code somewhere in the page is put there by the developer, or has been injected by the attacker.

I know that this is only a very brief summary of how CSP works, which suffices if you’re already familiar with CSP. If your head is spinning at the moment, it may be useful to check out this great tutorial by Mike West, where CSP is covered in a lot more detail, with more examples.

default-src 'self'
script-src 'self' http://platform.twitter.com https://syndication.twitter.com
img-src 'self' data: https://syndication.twitter.com http://platform.twitter.com https://*.twimg.com
style-src 'self' http://platform.twitter.com https://*.twimg.com 'unsafe-inline'
frame-src 'self' https://syndication.twitter.com https://platform.twitter.com

A sample CSP policy that heavily restricts where resources (scripts, images, styles, frames) can be loaded from.

Nope, Still not Convinced about CSP

Maybe the the promise of putting you back in control over your application is not enough to convince you to deploy CSP. However, next to this primary goal of CSP, plenty of additional security features have been added in subsequent versions as well. We’ll talk a bit more about these features in a future post, but if you’re impatient, go look for the upgrade-insecure-requests directive, or the sandbox directive. All powerful security features, right there in CSP.

In a final effort to convince you that CSP is definitely here to stay, take a look at these examples:

  1. Dropbox has added CSP support to their main website, through which you can access your files. Definitely take a look at their experience report.
  2. Yahoo has added CSP support to many of their services, and they explain how they did it.
  3. Github also deployed a very extensive CSP policy, and they provide a lot of details on how they got there.
  4. Mozilla has implemented CSP for their addons site, as documented here.

By now, you’re probably all excited about CSP, which is exactly where I want you to be. Keep reading to learn how to deploy CSP without breaking your stuff.

Deploying CSP without Breakage

By now, you’ve probably realized that CSP is powerful enough to completely mess up your application. Even for a fairly simple, mostly static website like this one, a simple CSP policy of default-src 'self' generates a heap of errors. Yet, I managed to construct a useful CSP policy without breaking anything, and it wasn’t that hard.

Even simple sites generate a lot of CSP errors at first, but they quickly disappear after some finetuning.

As you can imagine, configuring your CSP policy in a trial-and-error mode is not the best approach. There are a few steps you can take to smoothen the process , to make it iterative and ultimately successful. Let’s go over each of the steps below, and put it back together in the big picture at the end.

Creating a CSP policy without typos

Surprisingly, typos are a serious annoyance when building a CSP policy. If you put a colon after a directive, or misplace the single quotes, or even make a subtle typo in a URL, the effect will be something different than what you expect, and you’re left scratching your head as to why this is happening, well illustrated by CSP expert Scott Helme earlier this week.

Fortunately, there are a few tools that help you to compose the CSP header. They allow you to select directives and specify your hosts, and out comes the entire CSP header that needs to be deployed. Two good examples are the CSP builder tool at report-uri.io, and the CSP generator at cspisawesome.com.

Quickly constructing your CSP header using the policy builder at report-uri.io.

Deploying CSP without actually blocking any violations

CSP actually has two deployment modes:

  1. Blocking mode: In this default deployment mode, any resources that violate the policy will be blocked, and not loaded or executed.
  2. Report-Only mode: In this mode, the policy is checked by the browsers, and violations are reported, but not blocked.

So, if you start building your policy in report-only mode, you get all the errors without any of the nasty side-effects of blocking resources. Of course, to actually benefit from the protection offered by CSP, you’ll need to switch from report-only mode to blocking mode at some point. The right time to make that switch is when your policy is complete, and no more errors are being flagged.

To deploy a CSP policy in blocking mode, you use the Content-Security-Policy header. To deploy the same policy in report-only mode, you simply change the name of the header to Content-Security-Policy-Report-Only.

Learning about policy violations across your userbase

Not only does CSP offer you a way to run the policy without blocking anything, it also allows you to instruct the browser to send a report about the violation to a reporting endpoint. This means that when the browser detects a violation, it will send some information about the cause of this violation to the endpoint you specified, so you can collect this info from within your user’s browsers. This gives you great insights in potential policy violations, and allows you to identify potential security issues.

    "csp-report": {
        "document-uri": "https://dev.websec.be/blog/emberjsmeetup-security/",
        "violated-directive": "script-src 'self' https://ajax.googleapis.com/ajax/libs/webfont/1.5.18/webfont.js  https://www.google-analytics.com/ https://platform.twitter.com/ https://syndication.twitter.com https://websec-dev.disqus.com https://websec-be.disqus.com https://*.disquscdn.com",
        "effective-directive": "script-src",
        "original-policy": "default-src 'self'; script-src 'self' https://ajax.googleapis.com/ajax/libs/webfont/1.5.18/webfont.js  https://www.google-analytics.com/ https://platform.twitter.com/ https://syndication.twitter.com https://websec-dev.disqus.com https://websec-be.disqus.com https://*.disquscdn.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com/ https://platform.twitter.com https://*.twimg.com; img-src 'self' data: https://www.google-analytics.com/ https://syndication.twitter.com https://*.twimg.com https://platform.twitter.com; frame-src https://platform.twitter.com https://syndication.twitter.com https://disqus.com/; font-src 'self' https://fonts.gstatic.com; connect-src https://links.services.disqus.com; report-uri https://websec.report-uri.io/r/default/csp/reportOnly",
        "blocked-uri": "eval",
        "source-file": "https://a.disquscdn.com",
        "line-number": 1,
        "column-number": 1609,
        "status-code": 0

An example of a CSP report. It contains information about the page where the violation occurred, which directive was violated, what the CSP policy for that page was, and which code actually caused the violation.

To enable the reporting feature in CSP, you need to specify a report-uri directive with a URL to a reporting endpoint. You can write your own endpoint to collect the data, but you can also use the freely available report-uri.io endpoint, which collects your CSP reports for you. We’ll cover the reporting feature in more detail in an upcoming post.

Stand on the shoulders of giants

As always, there’s no need for you to rediscover all the peculiarities of deploying CSP on a website that uses external components. Others have already explored that territory, and have documented their solutions. We’ve already referenced the great write-ups by Dropbox, Mozilla and Github, which already give you a head start. In the coming posts in this series, we’ll go into detail on the CSP policy deployed on this website, and I’ll give you some recipes to deal with Twitter widgets and Disqus comment systems.

Integrate CSP in your development process

A last, but nonetheless important step, is to integrate CSP into your development process, right from the start. If you ignore CSP throughout the development phase, and only start creating a policy when the application is finished, it’s very likely that you’ll find your application to be incompatible with CSP. If you heavily depend on inline code or style blocks, or inline event handlers on HTML elements, you’ll bump into one of CSP’s major restrictions. Solving such issues at this stage in the development lifecycle either means rewriting parts of the application (which won’t happen), or relaxing the restrictions imposed by CSP (which undermines the entire point of deploying CSP).

A good example of how you can integrate CSP right from the start is the EmberJS framework. If you initialize a new EmberJS project, the toolchain will automatically configure a default report-only CSP policy, forcing you to deal with potential CSP violations right when you introduce them. This approach helps you to iteratively build your CSP policy, and to avoid mistakes that would make your application incompatible with CSP.

The step-by-step guide towards CSP

As you can see, there are a lot of things that will actually help you in deploying a useful CSP policy. Let’s put it back together in the big picture, a step-by-step guide you can follow to deploy your own CSP policy.

  1. Avoid wasting time hunting silly typos and use a CSP policy builder or validator
  2. Start with a CSP policy in report-only mode, so you don’t mess up your site when violations occur
  3. Iteratively finetune your policy, untill all violations in your developer console are resolved
  4. Deploy this policy in report-only mode to your users, and keep an eye on reporting for potential issues
  5. If necessary, further finetune your policy for optimal compatibilty with your user’s browsers
  6. If the number of reports has dropped below a certain threshold, flip the switch and enable the policy in blocking mode
  7. Keep an eye on your reports and investigate violations. Further finetune the policy when needed (e.g. when the behavior of a third-party widget changes), and resolve any vulnerabilities that have caused an injection attack.

This is essentially all you need to get started. I am well aware that at this point, these steps seem a bit hand-wavy and vague, and all is easier said than done. That’s exactly why we will dig deeper into CSP in the coming posts. I will give a detailed account of how I deployed CSP on this website, and how you can adapt your CSP policy to support various third-party widgets (Google Analytics, Twitter, Disqus, …).

Have you already taken a stab at deploying CSP? Did you succeed, or was the experience so frustrating that you gave up? I’m interested to hear about your experiences in the comments below!


Comments & Discussion