How to implement SRI in your build process

Imagine getting a call from a customer who says your website is serving malware. Your heart drops, you start sweating, and then the tweets start pouring in. Something is up. You find out your systems have not been tampered with.

In fact, it was your CDN provider that got hacked, and the scripts you included on your website have become malicious. You tell your customers what happened and they don’t care. You failed to provide a secure product, now trust is lost. If this happened 2 years ago, I would feel bad for you. But if it happened to you today I would sigh and say,“You should have used SRI.”

Subresource Integrity (SRI) is a new-ish web application security standard and W3C spec that helps prevent situations like the one above. Think of SRI as a safety net or rock-climbing rope for website content security. It is simply an attribute named integrity with a SHA-2 hash as the value within a <script> or <link> tag. For example:

Support

File types

The first version of the SRI standard was designed to address the biggest attack vectors caused by subresource hijacking on CDNs prevalent in Cascading Style Sheets (CSS) and JavaScript (JS) files. Other file types such as Flash, image, and video are not currently supported, but may be added in a future revision of the spec.

Please note, SRI will still not work with browsers such as Firefox for iOS. This is because Apple requires all apps (including browsers & in-app browsing) to use the WebKit web browser engine. It would be nice to see a status update…

SRI in your build process

We decided to show a real world example of SRI in a build process. We used a Grunt plugin called grunt-sri to generate the hashes.

How we did it for jQuery:

When looking at including SRI integrity for code.jquery.com, we took a fairly simple approach compared to previous implementations. The code base was already using Grunt, and as such, it was pretty straightforward to include a target using the popular grunt-sri node module. grunt-sri traverses a specified list of files and generates a JSON payload including all the metadata required for implementing SRI in a code base. Once generated, the output file can easily be used as the base datasource when building out an application. Here’s a simple example of implementing a base generator using grunt-sri:

Once implemented, running grunt sri:generate will output ./payload.json for requiring in your application, or another Grunt task. The SRI SHA can then be accessed from the payload file from your code as shown in the grunt-sri documentation:

For examples of generating SRI SHAs in various other platforms, see this Gist.

Conclusion

Subresource Integrity is a very simple way to secure static assets hosted on servers you have no control over. There are several tools that allow you to easily integrate SRI support into your build process(es). Modern website/application developers should not only do their part in implementing SRI, but discuss it with their peers explaining the benefits.

Justin is MaxCDN’s Director of Developer Relations and is responsible for evangelizing the company’s technologies and championing the needs of developers who use the network. He started BootstrapCDN in 2012 and is heavily involved with the FOSS community contributing to Bootstrap, Font Awesome, Grunt, Ionic, jQuery Foundation, Twemoji, Nginx & GNU Bash.

Joshua is an SRE at Heroku with over 20 years of experience Development and Systems Engineering. He took over the lead developer role on BootstrapCDN in 2013 and is heavily involved in open source and the community, both through his own projects and his contributions to others.

The Fetch API is now used internally for all new and future requests internally withing the browser, so in newer versions of the SRI spec it would be just plumbing in new syntax to connect to this (in the order of highest security risk etc)

This is all however future work to be done, however the current specification gives the ability to remove the risk of static assets for developers happier to use it.

Will this cooperate with the cache so a request for “http://code.jquery.com/jquery-2.2.3.min.js” might use a cached file from “https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.3/jquery.min.js” if the hash matches?

Not in the current iteration of the spec, there has been talk about improving caching but it wasn’t put in the first iteration.
Cross origin the answer is likely not to be possible due to cookies, headers and other issues being cached.

Why would cookies be a problem? A browser is not under any obligation to perform all requests (and isn’t the crossorigin=”anonymous” meant to indicate that e.g. cookies are not important). And as the crystallographic hash is given the browser should be able to assume that, if it has content with a matching hash, it will be the same.

If the headers can be used to change the meaning of the content (e.g. a files byte content might have different meaning in utf-8 and utf-16) and the hash do not include the headers. Then the hash is already useless as it doesn’t prevent the third party delivering something with a different meaning than intended.

The hash really isn’t useless, it prevents files of a different content from loading on your site.
The headers is an issue if we wanted to cache the response from one server and give it to a completely different site.
The headers have limited impact but sharing them across origins without the developer knowing from cache could cause encoding issues for example.

I don’t really see the threat. I believe the relevant part of the linked text is “Content injection (XSS) may then use a previously stored hash so that it looks like you are hosting the evil JavaScript payload that the browser has previously seen on evil.com.” But if your site is vulnerable to XSS you are already hosed (or using CSP in which case you could just white-list all the known trusted cdns) and otherwise you still have to put the hash of the evil JavaScript on your site.