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:

<script src="http://code.jquery.com/jquery-2.2.3.min.js" 
    integrity="sha256-a23g1Nt4dtEYOj7bR+vTu7+T8VP13humZFBJNIYoEJo=" 
    crossorigin="anonymous">
</script>

In order for SRI to work, the CDN needs CORS enabled. That (and more) is explained in detail in a previous Hacks post introducing SRI.

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.

Browsers

As of April 2016 CanIUse.com reports that ~52% of browsers (mobile + desktop) globally support SRI. The desktop user-agents that support SRI are Firefox (44+), Chrome(47+) & Opera (36+). The following Android-only mobile browsers also support SRI: Chrome (49+), Android Browser (49+), Opera (36+) & Firefox (45+).

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

There’s more than one way to get SRI into your build process. A number of tools already have plugins, for example: Grunt, Gulp, Broccoli, Webpack, Ember CLI & Handlebars.

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:

// Gruntfile.js
grunt.loadNpmTasks("grunt-sri");
grunt.initConfig({
    sri: {
        generate: {
            src: [ 'public/**/*.js', 'public/**/*.css' ],
            options: { algorithms: [ 'sha256' ] }
        }
    }
});

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:

// ES6 from https://github.com/neftaly/grunt-sri#javascript
var payload = require("./payload.json");
var sri = (id) => payload.payload[id];

var element = `<link
    href='/style.css?cache=${ sri("@cssfile1").hashes.sha256 }'
    integrity='${ sri("@cssfile1").integrity }'
    rel='stylesheet'>`;

For additional implementation details see https://github.com/neftaly/grunt-sri or peruse our pull request to code.jquery.com, specifically this diff.

Beyond JavaScript / Node.js

If you prefer to keep it old school with a Makefile, no problem. Assuming you are using a unix-like environment, you can skip using node modules and do something like this:

# Makefile
generate:
    cat FILENAME.js | openssl dgst -sha256 -binary \
        | openssl enc -base64 -A

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.

Big thanks to Frederik Braun, Jonathan Kingston, Francois Marier & Havi Hoffman for their help reviewing this article.

View full post on Mozilla Hacks – the Web developer blog

VN:F [1.9.22_1171]
Rating: 0.0/10 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)
Tagged on: , ,

6 thoughts on “How to implement SRI in your build process

  1. Jonathan Kingston

    I was referring more to the response cache, the hash is regarding the response body of the request it doesn’t factor in anything else in the response.

    If one jquery.com and cloudflare.com origins used different response headers we might end up serving the wrong response headers if we overly reused cache.

    There is also crossorigin=”use-credentials” mode which can send credentials for for CORS requests to the server.

    VA:F [1.9.22_1171]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.22_1171]
    Rating: 0 (from 0 votes)
  2. Anders

    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.

    VA:F [1.9.22_1171]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.22_1171]
    Rating: 0 (from 0 votes)
  3. Jonathan Kingston

    There is talk of other ways to use SRI, for example with @imports in CSS and module code in JS. How that might be implemented is still a long way off.

    The Fetch API which is the new drop in replacement for XHR has the ability to specify an SRI hash using the integrity attribute in the request: https://fetch.spec.whatwg.org/#concept-request-integrity-metadata

    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.

    VA:F [1.9.22_1171]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.22_1171]
    Rating: 0 (from 0 votes)
  4. Jonathan Kingston

    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.

    VA:F [1.9.22_1171]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.22_1171]
    Rating: 0 (from 0 votes)
  5. oxdef

    We still have risks if external script attempts to load another one via RequireJS or evaling() some response, don’t we?
    Anyway this is an interesting web application defensive approach.

    VA:F [1.9.22_1171]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.22_1171]
    Rating: 0 (from 0 votes)

Leave a Reply