Now is the time to seriously consider your Content Security Policy

News stories about hacking and malware are depressingly common these days. Here in the UK I think we’ve not really dealt with all the fall-out from last May when parts of our health service (along with many other businesses) were hit by the “Wannacry” ransomware worm. Yet another security incident happened the other day which will get less press coverage (since it’s nowhere near as dramatic) but has bigger implications for us as web develelopers…

Mining cryptocurrency is all the rage…

Since the insane financial bubble around Bitcoin started impinging on the mainstream media, a new online attack has surfaced: Injecting cryptocurrency mining software into people’s web browsers. The theory is pretty simple: If you can push the code into as many web browsers as possible, you stand a better chance of receiving coins in return. So there have been a spate of script-injection attacks against websites with vulnerabilities that allow for XSS to succeed.

But this weekend we saw a new scale of attack. Security researcher Scott Helme pointed out that a huge swathe of websites had been hit, including a variety of UK government sites. It caught my attention because Manchester City Council was on the list, and I walk past their town hall on my way in to Kagool‘s offices:

What happened here, and why should we pay attention?

The attack vector was actually very simple. Around the world, many websites have been making use of an javascript-based screen-reader framework called BrowseAloud. It’s a simple to use extension that helps people who have trouble reading your website by using text-to-speech APIs to read out content. Since it’s easy to use, it’s been fairly popular. You just include a reference to their script in your page to get started…

The hackers responsible for this attack found a flaw in the website hosting BrowseAloud’s script. They attacked this, and added a little bit of extra javascript to run the “CoinHive” mining script whenever BrowseAloud ran. Since the Terms & Conditions for BrowseAloud said “you must reference our script from our servers”, pretty much everyone using it would have been affected by the hackers change – so all their users started running CoinHive every time they browsed the affected websites:

Why is this bad, you might ask? Yes, some bad people made some free money but no personal data was compromised?

Just stop for a minute and think about what might have happened if the black-hats involved in this had done something subtler instead. Mining cryptocurrency is kind of obvious – it chews up lots of CPU. Your PC’s fans spin up and the CPU usage bar in Task Manager jumps up high. So it’s pretty easy to spot.

But what if they’d added a few lines of code to “phone home” whenever the script saw credit card fields on the page? Or had posted any email addresses typed in back to a central server? Or had captured passwords typed in to login forms? These are all achievable by javascript injection attacks, and they can all have much more significant personal impact than your CPU getting a bit warm…

What should we (as web developers) do about this?

The time has come people: You need to learn about Content Security Policy.

There’s now reasonable cross-browser support for technology aimed at mitigating exactly this sort of script-editing or injecting attack. Yes, it makes your life a bit more complex as a developer, but as this hack shows, the results are well worth your effort.

When you serve pages from your website you can send headers (or metadata elements) to the browser which define a security policy for external resources like scripts. The security has different levels, but broadly you have the choice of:

  • No policy – This is where you are now. Vulnerable to injection attacks because the browser will run any script it encounters in the page.
  • Specify where script can be loaded from – Only scripts hosted on specific domains will load and run. Generally defaulting to the same domain as the web page was loaded from, but can be extended to load external domains too. It doesn’t care what the script is, just where it is loaded from.
  • Scripts must be tagged with a specific value to run – You randomly generate a value (referred to as a “nonce”), stick it in your HTTP headers, and in an attribute of any script tags you inject into the page. Only script blocks with the value matching your header will run, but what’s in the script block doesn’t matter.
  • Scripts must have a valid hash to run – Every script in your page must have an attribute which contains a cryptographic hash of the block’s contents. If the browser’s calculated hash matches the one served by your site, the script can run. Otherwise it’s blocked. Hence only the content, not the source of the script matters.
  • No inline script – Script embedded in the html are disabled, and the browser won’t run them at all. Only references to external script are allowed.

Obviously the first one here isn’t that helpful for most real-world web development… Implementing the others (in whatever combination makes most sense for your site) requires some work on your part. There are some good blog posts by Troy Hunt on the basics of CSP and how he’s implemented it on his own website which are good reading to get started with these.

(Troy also has a good write-up of this whole hack issue that you probably want to read too)

Edited to add: In the comments below, Scott reminds me of the other thing that you need to think about here: Paying attention to what’s happening as a result of setting up your CSP. Wouldn’t it be great to know what’s happening out in the real world when the internet’s browsers hit your site? The magic you need here are the browser events around SecurityPolicyViolation. These get raised when your CSP finds something that violates the policy you defined. Your browser can be set up to do stuff with this data data, but one of the easiest things to do is notify a service called Report URI when events are raised. That’s a tool Scott has built to let you see details of the data collected, so you can spot any mistakes you may have made in your setup that trigger voliations by accident, as well as any attempted security violations that occur because of stuff going on out on the internet. The basic tier of the service is free, so it’s well worth talking a look at to see if it can help you.

So…

Next time you’re working on a website, have a think about what damage could be done to your site’s reputation by issues like this. Take the time to consider CSP, and the risks it can mitigate for you. These attacks will only become more common I suspect **.

And, if you’ve not done so already, have a read of Bas Lijten‘s post about CSPs in Sitecore. If you’re working with SXA you can also take a look at Michael West‘s extension to let you use hashes when you reference scripts on CDNs.


Edited to add::

** I wrote that statement above, and then later the same day I saw this tweet where Troy Hunt links to a thread about another significant attack in the same style as the one described above:

And we’ll only see more…

7 thoughts on “Now is the time to seriously consider your Content Security Policy

    • This is a debate we need to start having with clients. We’ve largely won the “you need to have https” debate, now we need to get to work on the “uncontrolled javascript is bad” debate. I’m not saying it’ll be easy – but the risks are getting bigger and clients need to start understanding that…

  1. Erm, I’m not sure CSP would have helped in this instance. The affected sites WANTED to load the script that was compromised. The script would’ve been whitelisted in a CSP.

    What they really needed was Subresource Integrity (SRI) – a related, but separate (and also cool) technology where the reference to the third-party file also has a file hash. If the file downloaded by the browser doesn’t match the hash, well, then it’s blocked.

    All that said… a Content Security Policy should also have been used in the event that someone injected script into the page.

    And, for what it’s worth, I look forward to the day that we also have an SRI-fallback option – so we can load jquery, etc., from a CDN, but then if the SRI fails (for example, if it is compromised, or heck, if the CDN goes down) we can fall back to loading from a local copy.

    • Good point. I came at it from that angle because so many websites I look at (and work on) are using none of these things right now. But perhaps I should make that clearer. One for my task list…

  2. Pingback: Sitecore snuck in Content Security Policy! | Jeremy Davis

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.