How I learned to stop worrying and love CSPs

To gain precise control over what styles, scripts and other assets do on your site, you can serve pages with a Content Security-Policy. What does that mean for the front-end, and for those of us building user interfaces in browsers? Well, it can be tricky to set up, but there are useful benefits.

Why CSPs?

CSPs basically work like a whitelist of asset domains that the browser will accept for your site. This is great, as browsers have no notion of benevolent or malicious assets. Only website owners do. By telling browsers where the good stuff is located, website owners can rest assured users of modern browsers won’t get bad assets executed. They are one out of many security headers.

CSPs have been around for ages (see for example the Twitter blog in 2011), but usage numbers seem to be quite low. It seems to me they have recently gained more traction though, as more people get interested into website safety and content integrity. See also how Mozilla Add-Ons did it.

How they work

Technically, a CSP is a string that contains key-value pairs, served as a header with the response (or via a meta element in the page). They describe for each type of content how it can be used within your page. To be more precise: where it can be loaded from. Definitions for each type of asset are separated with semicolons, the definitions themselves have spaces in between.

Here’s an example:

script-src https://hidde-cdn.com; style-src https://hidde-cdn.com

This will only allow scripts or styles if they are served from https://hidde-cdn.com.

There are also a number of keywords.

  • self will allow assets from the same domain that the page is served from
  • unsafe-inline will allow inline assets

See for example, this policy:

script-src 'self' https://hidde-cdn.com; style-src 'unsafe-inline' https://hidde-cdn.com

This allows scripts only from the current domain or from https://hidde-cdn.com, and allows styles inline or when served from https://hidde-cdn.com.

There are also directives: font-src for fonts, img-src for images,connect-src for fetching/requests and default-src if you want to set a default. Best set default-src to none, so that you already have a policy for any asset types you don’t or forget to define.

The unsafe-inline directive is interesting: it has the word ‘unsafe’ in it, because inline assets are considered harmful. I should clarify: inline assets aren’t harmful because they are inline as such, but it is the case that whenever malicious scripts or stylesheets are injected, they are likely to be inline. Blocking their execution altogether mitigates their risk. If that feels like too much, it is also possible to allow some inline scripts: identify one script with a nonce (a number used once) and whitelist that nonce.

In summary, a CSP lets you whitelist locations to load assets from. Although these whitelists themselves are simple when you grasp the concept, their consequences can be unexpected and, for front-end devs, rather inconvenient.

Some side-effects

When I recently implemented a CSP in a project I was working on, I found a couple of surprises that were inconvenient, some of them related to disallowing inline assets. Note that those specific issues will be go away if your setup has a different, less stringent CSP locally.

Adding styles via Developer Tools

Sometimes when I’m working on some CSS, I’ll inject CSS via the Dev Tools, so that I can see what the effect of my changes are without actually making them just yet. If your CSP disallows inline styles, you are out of luck as this feature will stop working.

Browsersync

If your local development environment uses script to reload the browser when you’ve changed a file, this likely uses inline scripts that will be blocked when your policy forbids inline scripts.

Analytics

If you’re using external analytics scripts, don’t forget to add the domains that they load from, if they are different from your own domain. This is the case for Google’s Analytics and Tag Manager products, for example.

Polyfills that inject CSS

The inert polyfill injects some CSS into the page in order to prevent user selection on inert elements. Injected CSS counts as inline CSS (obvs), so that will not work.

Inline styles in SVGs

Double-check that SVGs that you are including in your page do not contain style attributes, as some browsers can deem those to be a violation of unsafe-inline.

The good and the bad

What I love about CSPs

The great thing about CSPs is that, if implemented well, you know exactly where to expect attacks. Without a CSP, the ‘attack vector’ is unknown and likely big or of infinite size, with a CSP you know where it can come from.

If you work in a large organisation where the marketing team can insert scripts via Tag Manager(-like) solutions, which is quite common these days, CSPs are also a useful treshold. Scripts that are useful for gaining marketing insights could at the same time be risky from a security standpoint (not to mention privacy).

There are some design choices that make CSPs a joy to work with, for example the built-in report-uri directive, that lets you specify a URL to report CSP violations to, which can be used to track violations using a service like Sentry.

What I slightly dislike

If your site is served over SSL and you sanitise all the things (in other words, you avoid Little Bobby Table scenarios), your CSP does not actually make it more secure. Note that this is a huge if. If you’re healthy, a health insurance isn’t going to make you more healthy, but it is extremely sensible to have one in place anyway. This is kind of the point of CSPs, they provide extra cover for if things like SSL and XSS protection aren’t (correctly) in place. Mistakes can be easy to make in the security space. So I’ve learned to love this: CSPs don’t harm if you can work around the side-effects.

Something else I don’t really like is the usability of error reports for CSP violations in browser Dev Tools. They will make clear that there is an error, but aren’t too helpful in pointing towards the right direction. Browsers could be clearer about which exact bit of your policy is stopping an asset from working.

TL;DR

CSPs can nullify what XSS attackers can do once they’ve managed to attack. This is great, although implementing it can make some things harder on the front-end. But that’s ok, it is our job after all. For help with implementing a CSP for your site, check out Mozilla’s Laboratory Add-On and Google’s Web Fundamentals page on CSP.

Thanks Scott Helme, Jan van Hellemond, Krijn Hoetmer and Tim Severien for feedback on (earlier drafts of) this post.

Comments, likes & shares (1)