All websites have something worth protecting. Those valuable things are frequently loaded from a CDN (Content Delivery Network) which is a distributed network of data centers that deliver assets based on geographic locations of the user.

Using a Content Delivery Network to deliver content on your website has its perks. The main advantage of using a CDN is improved performance – speed matters because if your website is slow, it could frustrate your users sending them elsewhere.

Content Delivery Networks – The Basics

A Content Delivery Network allows you to incorporate javascript files (and stylesheets too) into your website like this:

<script src=”https://code.jquery.com/jquery-3.2.1.slim.min.js”></script>

The above code refers to an external script from code.jquery.com – a CDN is a third-party resource. Including things from external resources into your website is risky though: How do you know if the resource you’re loading into your website hasn’t been tampered with? Of course, you could trust the provider, but that’s not the point I want to emphasize here; the CDN you’re loading scripts from could get compromised and the script you’re loading into your website could have been altered to redirect you to a phishing website or contain a keylogger, for example. What would you do then?

A real world example

Incase you did not fully understand me yet, I will focus on an example: A company called TextHelp had developed a plugin called Browsealoud whose primary audience is visitors with reading difficulties. Almost two months ago, their software got compromised. After the compromise, one of their scripts got modified to contain a cryptocurrency miner. From that point, everyone who visited a website that embedded Browsealoud ran the cryptocurrency mining code. Who was affected? Oh, just a couple thousand websites – governments, courts and the National Health Service included.

Subresouce Integrity

The exact same script can also be incorporated into your website like this:

<script src=”https://code.jquery.com/jquery-3.2.1.slim.min.js” integrity=”sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN” crossorigin=”anonymous”></script>

The above code loads the exact same script, but there’s one difference: if the library would have been modified, it would no longer execute on your website stopping attacks such as the one outlined above – this is possible because the script now has an integrity attribute which specifies a cryptographic hash of the resource that is being retrieved. If the library you’re loading into your website would be modified, the hash of the file would be different and the browser would refuse to run it. This is a great defense – now you should probably make sure all the scripts you’re loading into your website have this attribute. A Content Security Policy can help you achieve this:

Content-Security-Policy: require-sri-for script;

The same can be applied for stylesheets:

Content-Security-Policy: require-sri-for style;

Or you can require SRI for both scripts and styles at the same time:

Content-Security-Policy: require-sri-for script style;

Generating the SRI hash

If you’ve incorporated the two headers outlined above, all your scripts and stylesheets are now required to have an integrity attribute. How do you generate the hash though?

This is pretty simple: head over to SRIHash.org, paste the link to your file and click “Hash!”

You can also visit CDNJS and copy the script tag with an SRI attribute:

Alternatively, the hash can also be generated using OpenSSL:

cat jquery.min.js | openssl dgst -sha384 -binary | openssl enc -base64

The fallback

If you have an integrity attribute, someone compromises the CDN you’re loading assets from and changes the file to do something malicious, the resource would be blocked. That’s great, but this also means that while the resource is blocked, your website could be left without javascript or stylesheets.

This problem can be easily eliminated by adding another line of code below the script tag:

<script>window.jQuery || document.write(‘<script src=”path/to/your/js/jquery.min.js”><\/script>’);</script>

This way, even if the original resource is blocked, your website would still work as usual because the script would be loaded from your website and not the CDN.

Lessons learned