Dynamic JavaScript and SRI


Some external JavaScript libraries are dynamic. That's a problem for the SRI model of security. How can this be fixed?

Definitions

Suppose I want my website to have the latest version of the jQuery library. I might use a Content Delivery Network (CDN) to serve the code for me.

HTML HTML<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

If an attacker were to get access to that CDN, they could inject code into my site and compromise my users' privacy.

This is not a theoretical problem. This is how British Airways and others were compromised. An attacker poisoned the external JS they were using and stole credit card details.

This is where SubResource Integrity (SRI) comes in. Rather than just linking to an external script, I will also take a cryptographic snapshot of it (a hash). If the code changes, it will no longer match that hash, and the browser will refuse to run the external code.

This is what it looks like:

HTML HTML<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js" 
  integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
  crossorigin="anonymous"></script>

So far, so good.

Dynamic Resources

But not all external libraries are static. Take Polyfill.io - a clever service which dynamically returns code based on what browser is requesting it. I've friends who work at the company which publishes Polyfill. These are my personal thoughts. I'm picking them because they're a popular service.

For example, if I include this script on my page:

HTML HTML<script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>

Firefox will get served the following JS:

JavaScript JavaScript(function(undefined) {}).call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});

But Opera will get served a completely different script:

JavaScript JavaScript(function(undefined) {Object.defineProperty(Array.prototype,"values",{value:Array.prototype[Symbol.iterator],enumerable:!1,writable:!1});var Iterator=function(){var e=function(){return this.length=0,this},t=function(e){if("function"!=typeof e)throw new TypeError(e+" is not a function");return e},_=function(e,n){if(!(this instanceof _))r...

Because the contents are different, the SRI hashes will be different. That means we can't use the integrity attribute.

Solution?

As noted on Polyfill's GitHub page, the service...

sends a different content per User-Agent (and that's the point of the service), so for Subresource Integrity, website developers don't need a hash per URL, but per (URL, User-Agent) pair and they need to change the integrity attribute on a per UA basis

There's no sensible way to verify that the code being sent back hasn't been tampered with.

Conclusion

You should use SRI for all your external resources. If you can't, then you should either take a static copy and serve it yourself, or run your own dynamic service.

For example, The Guardian runs the Polyfill code on its own servers.

HTML source of The Guardian website. Polyfill is being loaded from their own CDN.

I trust the people at the Polyfill service not to act maliciously. And I trust them not to get hacked. But I shouldn't have to trust them. The same goes for any external service. Trust but verify.

Update! In 2024, Pollyfill.io was taken over and became part of a massive supply-chain attack.


Share this post on…

  • Mastodon
  • Facebook
  • LinkedIn
  • BlueSky
  • Threads
  • Reddit
  • HackerNews
  • Lobsters
  • WhatsApp
  • Telegram

2 thoughts on “Dynamic JavaScript and SRI”

  1. Nico Schmoigl says:

    I do not agree to your assessment of "That means we can't use the integrity attribute.": SRI supports having multiple hash values to be provided as attribute value for "integrity". So, you could provide the hash values of all reasonably-used User Agent types there. The browser would automatically pick&choose the right one for you. It may get challenging, though, to find the proper superset of all possible values...

    Reply

What are your reckons?

All comments are moderated and may not be published immediately. Your email address will not be published.

Allowed HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <p> <pre> <br> <img src="" alt="" title="" srcset="">