My Profile Photo

Sheogorath's Blog

Self-isolate your website with a CSP

on

Of course the headline is a little wordplay talking about the current situation, but also correct nonetheless. In order to harden your website and prevent unexpected leaking of your user’s IP addresses and browser fingerprints to third-parties, a CSP, Content-Security-Policy, is a great tool.

What is a CSP

“Content Security Policy (CSP) is a computer security standard introduced to prevent cross-site scripting (XSS), clickjacking and other code injection attacks resulting from execution of malicious content in the trusted web page context.” — Wikipedia

It essentially tells your browser what kind of content it’s supposed to run and what might appeared on the website due to a malicious injection. It has a wide set of instructions which allow a very granular configuration of allowed and forbidden third-parties. You can restrict where images or fonts are loaded from, where scripts come from and if inline-scripts are accepted. Same goes for CSS, you can restrict the installation of service-workers or you can block XHR requests. And that’s just a few of the configurable details. You can also report all violations to a trusted endpoint and this way become aware of possible attacks. All details can be found on Moznet’s CSP page.

A CSP can be set by HTTP header, or by an HTML <meta>-element. For simplicity and because it’s my preferred way, I’ll only talk about the HTTP header.

Endpoint definition

A CSP defines endpoints in form of a protocol, optionally along with a host or by using a special keyword.

  • https: — would allow all hosts that use the protocol HTTPS. It’s not recommended to keep policies that broad.
  • https://cdn.example.com — would allow this specific host (cdn.example.com) over HTTPS.
  • https://*.example.com — would allow any host over HTTPS under directly under example.com. e.g. cdn.example.com, www.example.com, test.example.com but not test.cdn.example.com
  • data: — would allow data URLs to be used. These are required when you add inline pictures into your HTML page.
  • 'self' — would allow loading things from the same-origin i.e. if your page is available at https://example.com to load content from https://example.com but not https://example.com:8080 or https://cdn.example.com
  • 'unsafe-inline' — used in the script-src and style-src policy to allow inline JavaScript and CSS.
  • 'unsafe-eval' — used in the script-src policy to allow the use of eval() in JavaScript.
  • 'none' — expresses this type of content shouldn’t appear on this website.

There are some more, but for our current needs, those are enough should be sufficient.

Writing a first policy

In order to write your first policy, I would either recommend writing a report-only policy and use a tool like Report-URI as endpoint for your violation reports, or set up a clone of your website, that you can use to experiment. The latter probably provides the better debugging.

In order to figure out what hosts should be provided, it’s a good idea to fire up your web developer tools and check the network tab. Then have a look at the two marked columns as below. The first marked column tells you the hostnames, that you will need to specify, the second marked column tells you the media-type.

W3Schools loading content from all kinds of third parties

This screenshot is of course an example from W3Schools and as you see, there is going on a lot of things. Stylesheets are loaded from maxcdn.bootstrapcdn.com as well as fonts.google.com and www.w3schools.com, there are scripts loaded from cdnjs.cloudflare.com, ajax.google.com and also www.w3schools.com itself.

In order to write a policy that the needs from this screenshot1 we would now extract the protocols and hosts first and assign them to the CSP of their media-type.

Content-Security-Policy: style-src https://maxcdn.bootstrapcdn.com https://fonts.google.com https://www.w3schools.com; script-src https://cdnjs.cloudflare.com https://ajax.google.com https://www.w3schools.com; img-src https://www.w3schools.com

Note: If you want to debug the policy using Report-URI, you write Content-Security-Policy-Report-Only instead of Content-Security-Policy as header and put report-to <your report-uri URL> at the end.

That’s a first step. But in order to make it less annoying, one can substitute all occasions of https://www.w3schools.com with 'self', which makes the CSP a lot more portable from development environments. As by my personal preference, in those policies I put 'self' usually first.2

Content-Security-Policy: style-src 'self' https://maxcdn.bootstrapcdn.com https://fonts.google.com; script-src 'self' https://cdnjs.cloudflare.com https://ajax.google.com; img-src 'self'

With this done, you can add the header to your website and start to check if something else is broken. If styles are broken, check the browser console, there are usually hints about why something was blocked. Most likely you might use inline-scripts or -style or eval(). In these cases, add 'unsafe-inline' or 'unsafe-eval' to the policy as first step. In long term, you should try to get rid of your inline-scripts/-styles and eval() usage.

Getting rid of Google using CSP

Main motivations for me to write this article, was to show you how to get rid of Google and other third-parties on your website even when an Update of a theme or similar may reintroduces them.

In order to do that, you can just remove them from the policy you specified for your website. In the example above this could mean the policy looks like this:

Content-Security-Policy: style-src 'self' https://maxcdn.bootstrapcdn.com; script-src 'self' https://cdnjs.cloudflare.com; img-src 'self'

Note: Depending on your website, this might breaks functionality.

Just by removing them from your CSP, the browser will automatically block all connections to Google Fonts and Google AJAX. This is of course not a perfect solution and you should try to remove them from the source code in first place.3 But sometimes that’s easier said than done.

A real world example is shields.io. It relies on Google Fonts and will share your IP and user-agent with Google every time you visit the page. And while I tried to fix this upstream, I set up shields.shivering-isles.com which blocks Google Fonts via CSP while running the upstream source code.

Yes, the fonts look different, but that’s something I can live with and hopefully some day, upstream will fix it.

Why you should have a CSP on your website

/e/ is an OS that claims to make google-free Android available for everyone and is rather successful. But today they produced a bigger faux pas, where it turns out their website is using Google Fonts and Google AJAX.

As they use Wordpress as CMS at this point in time, I consider two options as possible reasons how that happened:

  1. During the initial theme selection no one cared about privacy issues of the theme and that it relies on Google services.
  2. The theme was “degooglified”4 by some designer but an update of the theme reintroduced it.

Either way, it became obvious and a problem because things were fetched from Google on page load. A CSP that would only allow non-google domains, would have prevented this issue from becoming serious and only leave some blocked requests and maybe a broken look for the website.

Real self-isolation with a CSP

When you open the web developer tools and look at the CSP for this blog, you’ll notice that the CSP is rather minimal and doesn’t contain any external sources:

Content-Security-Policy: default-src 'none'; script-src 'self'; img-src data: 'self'; style-src 'self' 'unsafe-inline'; font-src data: 'self'; object-src data:; base-uri 'none'; form-action 'none'; worker-src 'self'; connect-src 'self'

It only employs 'self', data:, 'unsafe-inline' for styles and 'none'. This totally isolates the websites and makes sure no external requests are possible. In order to do that, all styles reside within the main page, all images are hosted on https://shivering-isles.com and all content is created with this level of security and privacy in mind. It’s still not perfect. unsafe-inline is still a bad policy, but better than no policy and will hopefully disappear soon.

Also having more than only your own webpage, doesn’t automatically make your website a worse website. Maybe you use a CDN or your embed some content from a trustworthy third-party, then it’s definitely fine to add those to your policy. The important factor and strong feature that a CSP provides to you is the ability to make this decision and being aware of what you add.

Conclusion

This article should mainly showcase why you want a CSP on your website and how you can implement a basic version of it. There is a lot more to learn about it. And I recommend reading some more about how a CSP works and how to create them. There are also generators and analyser out there to make your life easier.

Either way, having a CSP can protect you and your visitors from unexpected external calls, gives you more control over your external dependencies and can make it easy to fix small issues with unwanted behaviour of web-based tools.

Therefore, I hope this motivated you to deploy one on your website and see you another time.

  1. The page I opened there, actually loads even more scripts, but for the concept this should be sufficient. 

  2. Even when that’s considered impolite in other contexts. 

  3. Some general help with this can be found on Kev’s blog

  4. If you want to degooglify your theme, there are some great articles out there.