BIPI
BIPI

Subresource Integrity: The Cheap Defence Most Teams Skip

Cybersecurity

SRI adoption sits below 30% on third-party scripts in production. The polyfill.io incident showed exactly what that gap costs. Adding integrity hashes is a build-time change that takes hours and prevents class breaks.

By Arjun Raghavan, Security & Systems Lead, BIPI · September 13, 2024 · 6 min read

#sri#supply-chain#web-security

When polyfill.io was acquired by a Chinese company in February 2024 and started serving malicious payloads, around 100,000 sites were affected. The attack was simple: the new owner shipped JS that injected redirects on certain user agents and routed traffic to gambling and porn sites. Some payloads also stole session cookies.

Every affected site that had Subresource Integrity hashes on their polyfill.io script tag was unaffected. The browser computed the SHA-384 of the served JS, compared it to the integrity attribute, and refused to execute the mismatch. SRI cost those teams maybe an hour of build configuration. The teams without SRI spent weeks on incident response.

What SRI actually does

SRI lets you pin third-party scripts and stylesheets by hash. The script tag includes an integrity attribute with a SHA-384 hash plus the crossorigin attribute. The browser fetches the resource, computes the hash, and only executes if it matches. If the CDN serves different bytes (because of compromise, mistake, or version drift), the browser blocks the script and fires a securitypolicyviolation event.

The threat it addresses is third-party JS turning malicious between when you tested it and when your users load it. CDN compromise, DNS hijacking on the CDN, malicious version pushes by the maintainer, ISP-level injection on the path. SRI doesn't help with first-party JS you own, and it doesn't help if the third-party legitimately updates and you accept the new hash.

Why adoption is so low

Two reasons. First, SRI breaks when the third party updates. Every version of a CDN-hosted library has a different hash. If you pin to a specific version, you lock it forever. If you pin to a 'latest' URL, SRI fails as soon as the file changes.

Second, build pipelines don't generate the hashes by default. Teams using webpack, Vite, or Rollup need a plugin (webpack-subresource-integrity, vite-plugin-sri) that hashes assets at build time and injects integrity attributes. It's a one-time setup that nobody owns.

How to roll out SRI without breaking things

  1. Inventory third-party scripts: every script tag not pointing at your own domain
  2. Categorise: pin-able versioned URLs vs latest-tracking vs dynamic loaders
  3. Generate hashes via the SRI hash generator or 'openssl dgst -sha384 -binary | base64'
  4. Add integrity and crossorigin attributes to each tag
  5. Deploy to staging, watch for securitypolicyviolation events, fix mismatches
  6. Add a CI step that fails if a new third-party script tag ships without integrity

For libraries you load from a CDN like jsdelivr or unpkg, those services publish SRI hashes in their UI. Copy and paste. For third-party SDKs that ship 'latest' bundles (Segment, Stripe.js, Sentry), most explicitly do not support SRI because they ship updates frequently. For those, you have two options: self-host the SDK from your own CDN with a pinned version, or accept the SRI-less risk and compensate with strong CSP.

What to ship this week

Audit every script tag on your top three pages by traffic. Anything from a third-party origin without an integrity attribute is a candidate. Add hashes to the easy ones (CDN libraries with pinned versions) immediately. Open a ticket for the harder ones (analytics, payment SDKs) with explicit risk acceptance from someone senior.

The polyfill.io incident is the textbook example of why SRI exists. The next one is happening right now to a different third-party. The defence costs an hour. Most teams find out they didn't ship it after the breach review.

Read more field notes, explore our services, or get in touch at info@bipi.in. Privacy Policy · Terms.