Do not let your CDN betray you: Use Subresource Integrity

Mozilla Firefox Developer Edition 43 and other modern browsers help websites to control third-party JavaScript loads and prevent unexpected or malicious modifications. Using a new specification called Subresource Integrity, a website can include JavaScript that will stop working if it has been modified. With this technology, developers can benefit from the performance gains of using Content Delivery Networks (CDNs) without having to fear that a third-party compromise can harm their website.

Using Subresource Integrity is rather simple:

<script src="https://code.jquery.com/jquery-2.1.4.min.js"
integrity="sha384-R4/ztc4ZlRqWjqIuvf6RX5yb/v90qNGx6fS48N0tRxiGkqveZETq72KgDVJCp2TC"
crossorigin="anonymous"></script>

The idea is to include the script along with its cryptographic hash (e.g. SHA-384) when creating the web page. The browser can then download the script and compute the hash over the downloaded file. The script will only be executed if both hashes match. The security properties of a collision resistant hash function, ensure that a modification results in a very different hash. This helps the site owner detect and prevent any changes, whether they come from a compromised CDN or an evil administrator.

An important side note is that for Subresource Integrity to work, the CDN must support Cross-Origin Resource Sharing (CORS). The crossorigin attribute in the above code snippet enforces a CORS-enabled load. The anonymous value means that the browser should omit any cookies or authentication that the user may have associated with the domain. This prevents cross-origin data leaks, and also makes the request smaller.

Integrity syntax

As you may have noticed, the integrity attribute does not just include the hash value. It also contains the digest name. The syntax for the integrity attribute allows multiple tokens of this name-value format. This allows site owners to specify hashes of different strengths as well as the values of multiple scripts that may be behind a URL. This is useful for browser sniffing or content negotiation.

<script src="https://code.jquery.com/jquery-2.1.4.min.js"
integrity="sha384-R4/ztc4ZlRqWjqIuvf6RX5yb/v90qNGx6fS48N0tRxiGkqveZETq72KgDVJCp2TC
sha256-8WqyJLuWKRBVhxXIL1jBDD7SDxU936oZkCnxQbWwJVw="
crossorigin="anonymous"></script>

Failover

For the best performance, users would load all resources from the CDN, but if integrity cannot be verified, you don’t want your users to be trapped on a non-working web page. To make failover work, we recommend hosting a copy of the script on your own origin. To recover from failure one could then extend the previous snippet with the following code:

<script>window.jQuery || /* reload from own domain here */;</script>

This code will check if jQuery has been defined and could otherwise insert a script tag that loads the same origin version of the script.

Please note that many scripts update regularly, especially if they do not come with a version number. If you want to secure your CDN-loaded scripts, it is best to stick to a specific version and not use filenames with the word ‘latest’ in them.

HTTP or HTTPS?

Subresource Integrity works on both HTTP and HTTPS. If you are serving your page over plain HTTP, the browser can still figure out if the script was modified on the CDN, but it is not protected against active network attackers, as they would be able to just remove the integrity attribute from your HTML. It is, however, in the interest of your users to provide confidentiality, integrity, and authenticity of your web applications by using HTTPS for the entirety of your website.

Stylesheet support

While we are working on adding support for subresources other than scripts, you can also use Subresource Integrity for CSS. Just use the integrity attribute that you now know so well on your <link> tag!

Try Subresource Integrity Now!

If you want to test browser support or toy with examples, take a look at https://srihash.org/, which can do all the grunt work of computing hashes as well as checking if your CDN already supports HTTPS. A few early adopters like BootstrapCDN, CloudFlare and GitHub are already experimenting with it.
There is some additional documentation of Subresource Integrity on MDN. But if you want to read all the fine details of Subresource Integrity, take a look at the specification.

To conclude, Subresource Integrity can make your website safer when using a CDN that you do not fully control. It’s as simple as adding just a few extra attributes to your script tags.

About Frederik Braun

Frederik is a Security Engineer at Mozilla. His day job involves looking for security bugs in Mozilla products and related web properties. Frederik is passionate about all computer security topics. Beside his professional involvement in security, he also enjoys playing CTFs with his former fellow-students from FluxFingers.

More articles by Frederik Braun…

About Francois Marier

Security and Privacy Engineer

More articles by Francois Marier…


38 comments

  1. Jerry Qu

    Firefox doesn’t supports this:

    var code = ‘alert(“hello world!”);’;

    var blob = new Blob([code], {type: “application/x-javascript”});
    var blobUrl = URL.createObjectURL(blob);

    var script = document.createElement(‘script’);
    script.crossOrigin = ‘anonymous’;
    script.integrity = ‘sha256-0URT8NZXh/hI7oaypQXNjC07bwnLB52GAjvNiCaN7Gc=’;
    script.src = blobUrl;

    document.head.appendChild(script);

    I want use SRI to verify my code getting from localstorage, so I write the code above. It works fine in Chrome 45+.

    September 27th, 2015 at 01:28

    1. Frederik Braun

      Thank you Jerry!
      We will track this error in a Bug at https://bugzilla.mozilla.org/show_bug.cgi?id=1208875

      September 27th, 2015 at 11:50

  2. Francis Kim

    I wonder if the subresource integrity works for a protocol relative URL like ?

    September 27th, 2015 at 05:40

    1. Jerry Qu

      Yes, both Chrome 45+ and Firefox 43 support relative URL with SRI.

      And Chrome supports blob URL with SRI, Firefox doesn’t.

      September 27th, 2015 at 05:50

    2. Frederik Braun

      Subresource Integrity (SRI) works on every resource that is in the same origin (same protocol, host name and port) or has CORS enabled. No matter if the URL is relative or absolute. SRI happens after URL parsing.

      September 27th, 2015 at 11:31

      1. Francis Kim

        Thanks Jerry and Frederik!

        September 29th, 2015 at 01:58

  3. Steve Souders

    Subresource integrity is great and thank you for this article.

    Developers should avoid using document.write so it’s unfortunate that you fell back to that in your example. Can you offer a better workaround?

    Also, it would be nice to address how this affects resource sharing across websites. Your example is perfect for illustrating this. There are thousands of websites using “code.jquery.com” or “googleapis.com”. Can you confirm that subresource integrity works when the script is read from cache?

    Thanks, Steve

    September 28th, 2015 at 08:21

    1. Francois Marier

      The integrity checks do work whether the data is coming from the network or the cache.

      September 28th, 2015 at 15:58

    2. Frederik Braun

      Thank you for your feedback about document.write. I have rewritten the example.

      September 29th, 2015 at 01:21

      1. Steve Souders

        Thanks for thinking about how to avoid document.write. Unfortunately, I don’t think that revision is a good suggestion because you took a script that was synchronous and made it async. This is particularly bad since the script in question is jquery, so it’s likely that other scripts in the page are depending on it. If you fallover is reached then jquery now gets loaded async so the other scripts that *used* to be blocked will now load immediately and fail from undefined symbol errors.

        I can’t think of an easy, universal answer here. Using “defer” suffers from the same problem if the subsequent scripts are not also defer.

        Having good failover for JavaScript is a big issue and perhaps shouldn’t be tackled as a side effort in this post. Coming from the world of dynamically loaded C libraries where we always checked for symbols before accessing them, JavaScript’s casual assumptions of global symbol availability means we need a widespread change in programming practices to make fallover scenarios robust.

        September 29th, 2015 at 09:08

        1. Frederik Braun

          Thanks for your continued input :) I will leave the code, as an exercise to the reader then.

          September 30th, 2015 at 03:49

  4. Gerben

    “HTTP or HTTPS?”
    This would be ideal if the CDN doesn’t support HTTPS, while your website does. This way you prevent MITM attacks. Way more likely than a rogue CDN.

    September 28th, 2015 at 09:06

  5. JW

    This would be insanely more powerful if you could also provide a resource size. Its far more likely 19k jquery and 2meg jquery (injected code) can have similar hashes than 2 different 19k files. The nice thing, is that you can check sizes without heavy computational cycles. If the sizes do not match, then you don’t even need to check the hash. and it gives you far less possible hash collisions.

    September 28th, 2015 at 12:24

    1. Francois Marier

      If it becomes possible to easily find collisions using a particular hash function, then it’s time to deprecate that no-longer-secure hash function :)

      September 28th, 2015 at 16:04

      1. Paul Irwin

        Indeed. JW should read up on cryptographic hash functions. It is not computationally feasible to find a SHA-2 collision out of even 2^256 possible hashes that also manages to be valid malicious JavaScript.

        October 1st, 2015 at 10:00

    2. starbuck

      That would work under the rationale of performance (checking length first before calling cryptography primitives) but not on the rationale that collisions should be expected. If you expect collisions as a practical result of your hash function, then you should rather choose another hash function.

      October 1st, 2015 at 08:53

  6. Natim

    It might worth having something like that as well:

    Where “demo.sha384“ contains the result of “sha384sum demo.js > demo.sha384“

    September 29th, 2015 at 07:16

    1. Natim

      The script tag I wrote in my previous message did not make it through, basically I was asking if it would be possible to load the signature from a file (in order to ease hash updates)

      src=”//code.jquery.com/jquery.dev.js”
      integrity=”@jquery.dev.sha384″
      crossorigin=”anonymous”

      Also the signature are in base64 so my previous command won’t work to build the hash, here is the right one:

      cat FILENAME.js |
      openssl dgst -sha384 -binary |
      openssl enc -base64 -A -out demo.sha384

      October 1st, 2015 at 07:50

  7. Paul Masurel

    That’s awesome!
    I’ve been dreaming of this feature for a long time )(https://www.reddit.com/r/javascript/related/204tda/hash_in_script_tag/)

    Can it also open the door to cross site caching?
    ie. Can I eventually stop downloading the same version of jquery.min.js on different website if they share the same hash?

    October 1st, 2015 at 07:28

    1. Frederik Braun

      Unfortunately, this could be used as a cache poisoning attack to bypass Content Security Policy. See the section about “Content addressable storage” at .

      October 1st, 2015 at 07:38

      1. Paul Masurel

        I didn’t find the section you mentioned. I am not really familiar with the term “cache poisoning”, but if this is what I think it means, wouldn’t that require for the attacker to generate a collision ?
        There is no known collision for SHA-2. It will be broken some day. When it happens, it could get deprecated with the next update of browsers.

        Your document also talks about privacy issues : people could check that I visited nytimes.com because I already have one of their file in my cache.

        One way to address it might be to restrict by default the resource to a domain explicitely, in which case the hash(file) would be replaced by hash((restrictiondomain, content)).
        Library like jquery could be declared public, in which case the hash wouldn’t be namespaced.

        October 1st, 2015 at 08:10

        1. Frederik Braun

          The spam filter ate my link. See https://frederik-braun.com/subresource-integrity.html

          October 2nd, 2015 at 00:39

  8. S├ębastien Pierre

    I’m wondering if you are only accepting base64-encoded sha-NNN digest, or if we can use the hexdigest as well.

    October 1st, 2015 at 08:34

    1. Francois Marier

      Only base64 digests. The same as Content Security Policy Level 2.

      October 1st, 2015 at 15:50

  9. Joe Devon

    Would be cool to have it as part of the url instead so that you automatically cache bust newly committed code so that versioning and integrity are built in at once.

    October 1st, 2015 at 10:59

  10. Justin Dorfman

    I am really happy to see Google and Mozilla getting behind SRI. I hope others follow suit.

    October 1st, 2015 at 11:23

  11. Martin Uecker

    This is really cool, I alway wanted this feature. (Here is my attempt to write something down [1].)

    I still like the idea of being able to specify the hash in a media fragment. This would make it possible to use such URLs everywhere:

    http://www.foo.org/software.tgz@hash=57488f

    [1] http://www.eecs.berkeley.edu/~uecker/linkfingerprints.txt

    October 1st, 2015 at 13:35

  12. Josh Graham

    Great idea! I feel, though, the fallback URI should be declared in the tag, not an ad hoc programmatic solution.

    This could be another new attribute that is only used if there is an integrity failure:

    “`

    “`

    October 2nd, 2015 at 00:17

    1. Frederik Braun

      Yeah, we had this (and a lot of other things) in an earlier draft of our specification. We removed everything except the minimal functionality for the sake of progress. We will take this into consideration for SRI v2.
      But modifying how HTML works and then expecting the whole world (e.g., XSS filters) to catch up on how new attribute that can trigger script loads is tricky.

      October 2nd, 2015 at 00:43

      1. Josh Graham

        Yep, fair call. Even putting the fallback URI inside the integrity attribute value is not great.

        Still, for those doing it ad hoc, perhaps they could add a data-integrity-fail-src attribute to the script tag so at least the fallback URI is declared in the element. A general-purpose onError handler (I assume an onError event is generated when the integrity check fails?) for script elements could then inspect the data-integrity-fail-src attribute and inject a new script element with the fallback URI as its src.

        Again, great work and thanks for the write up!

        October 2nd, 2015 at 01:18

  13. Alex

    So when using https for the entirety of my website, is the external part meant to be inlcuded? When I do only https for html and use sri for all other parts, will the browser signal that something is wrong like it is doing without sri?

    October 2nd, 2015 at 06:26

    1. Francois Marier

      SRI does not affect the mixed content blocker. If you have an HTTPS website and you want to load third-party scripts, they also need to be served over HTTPS.

      October 5th, 2015 at 13:51

  14. Alex

    I’m asking myself, how this can be used by ad-networks to track users or to make it more difficult to block ads…

    October 2nd, 2015 at 16:26

    1. Francois Marier

      If you can come up with a concrete example, file a bug: https://github.com/w3c/webappsec-subresource-integrity/issues

      October 6th, 2015 at 14:39

  15. Alex

    How does this affect the perception of secure connections for the enduser?

    October 3rd, 2015 at 05:45

    1. Francois Marier

      SRI, just like Content Security Policy (CSP), doesn’t have any user-visible UI.

      October 6th, 2015 at 14:41

  16. pluto

    why not for images and other media, only scripts?

    October 3rd, 2015 at 13:59

    1. Francois Marier

      We will likely add support for other types of resources in future versions, but in the interest of shipping version 1 of the spec quicker, we decided to reduce its scope down to just scripts and styles.

      October 6th, 2015 at 14:43

Comments are closed for this article.