Web Security

Subresource Integrity: The Hash Tag That Protects Web Apps

May 13, 2026 8 min read Haven Team

A modern web page often loads JavaScript from places its operators don't control — a CDN, an analytics provider, a font service. Each of those is a potential attacker. Subresource Integrity is a small browser feature that lets a page say, in advance, "this script must hash to exactly this value, or refuse to run it." It works. It's also less than half a defense.


In June 2018, the British Airways website lost the credit card details of roughly 380,000 customers because an attacker modified a single third-party script the site loaded from an external host. The script — Modernizr, in this case — was injected with 22 lines of JavaScript that exfiltrated form data to an attacker-controlled server. The compromise lasted weeks.

This is the canonical example of a supply chain attack on a web application, and it's exactly the threat that Subresource Integrity (SRI) is designed to address. SRI has been a W3C recommendation since 2016 and is supported in every modern browser. Despite its age, the majority of web pages that include external resources don't use it.

The Mechanism

SRI works by attaching a cryptographic hash of the expected resource to the HTML tag that loads it. The browser fetches the resource, computes the hash, and compares it to the expected value. If they don't match, the resource isn't executed.

A typical SRI-enabled script tag looks like this:

<script
  src="https://cdn.example.com/library-v1.2.3.js"
  integrity="sha384-Cs3dgfk0Y9...zX7G/MgPa+Hw9Bz9z"
  crossorigin="anonymous">
</script>

The integrity attribute contains a base64-encoded hash of the resource, prefixed with the hash algorithm name. SHA-256, SHA-384, and SHA-512 are supported. The crossorigin attribute is required when the resource is on a different origin; without it, CORS prevents the browser from reading the response and SRI cannot function.

Multiple hashes can be supplied (space-separated). The browser accepts the resource if any one of them matches. This is useful for migrating from one hash algorithm to another, or when you want to allow either of two known-good versions during a rollout.

What This Actually Prevents

The narrow threat SRI prevents is unambiguous: an attacker who can substitute the contents of a resource you load — through a CDN compromise, DNS hijacking, MITM on a non-TLS path, or any other mechanism — cannot serve modified content without invalidating the hash. The browser will refuse to execute it.

Specifically excluded

SRI does not protect against an attacker who can modify your own HTML. If the page that contains the integrity attribute is itself attacker-controlled, the attacker will simply change both the script URL and the hash.

This means SRI's value is conditional on a specific architecture: your HTML must be served from infrastructure you trust strongly, and the resources it references must be served from infrastructure you trust less strongly. The asymmetry is real for most real-world setups — your origin server is small attack surface, but the CDNs and analytics services you embed are large surface — and SRI is exactly the right tool for that asymmetry.

Where SRI Falls Short

Threat SRI Helps?
CDN compromise serving modified library Yes
Man-in-the-middle injecting modified script Yes
Your own server is compromised No
Library's upstream developer adds malicious code, you update the hash No
Malicious npm package included at build time No (SRI doesn't reach build pipelines)
Script is loaded dynamically via fetch() + eval() No (SRI only applies to <script> and <link>)
Resource is loaded with attacker-influenced parameters in query string Hash covers byte-for-byte content, which can change with query params

The honest framing: SRI defends against the specific subset of supply chain attacks where a trusted-but-external resource is modified after you committed to its contents. It does not address the much larger class of supply chain attacks that operate further upstream — at the dependency, build, or developer level.

Operational Pitfalls

Hash drift on dynamic resources

SRI requires byte-for-byte stability of the resource. If your CDN dynamically compresses, transforms, or even just re-serializes the resource — for instance, if it auto-rewrites paths or strips comments — the hash will not match and the resource won't load. This is a frequent rollout failure for SRI: it works in development against an unmodified resource, then breaks in production when the CDN's optimization is in path.

Versioned-URL discipline

SRI only works if you load a specific version of a resource (e.g., library-v1.2.3.js), not a "latest" alias. If your script tag says library-latest.js and you write a hash for whatever the file contains today, the moment the CDN updates the file your page breaks. Pinning to a specific version is mandatory for SRI to be operationally viable.

Combinatorial explosion with many resources

Adding integrity hashes to every script and stylesheet on a complex page is tedious. Build tools like webpack, Vite, and Rollup have plugins that compute and inject SRI hashes automatically (webpack-subresource-integrity, rollup-plugin-sri, etc.). For a hand-edited static site, the friction is real but manageable; for a large dynamic application, automation is essential.

Reporting and observability

When an SRI check fails, the resource silently fails to load — there's no user-visible error and no automatic reporting to your servers. You can mitigate this with the Content Security Policy require-sri-for directive and CSP violation reporting, which tells your server when SRI checks fail. Without that wiring, the first you'll know is a customer support ticket.

How to Compute the Hash

For a one-off resource:

curl -s https://cdn.example.com/library-v1.2.3.js | \
  openssl dgst -sha384 -binary | \
  openssl base64 -A

Prefix the result with sha384- and you have the value for the integrity attribute. Most CDN providers and major library distributors also publish SRI hashes directly — jsDelivr, unpkg, and cdnjs all expose a "copy with SRI" option in their UI.

Where SRI Fits in a Broader Strategy

SRI is one layer in a defense-in-depth approach to web supply chain risk. A serious threat model also wants:

Defense in depth is not about stacking redundant defenses — it's about layering defenses that fail in different ways. SRI handles in-flight modification of pinned resources. CSP handles unexpected additions. Lockfiles handle pre-build substitution. Together, they cover the supply chain. Individually, each one has gaps that the others fill.

Where Haven Fits

Haven's web client (the React app served from havenmessenger.com) is bundled with Vite, and our production build embeds SRI hashes for all first-party resources. We deliberately minimize third-party script inclusion — no analytics, no ad networks, no embedded social buttons — which reduces the SRI maintenance surface to a small set of pinned dependencies.

The cryptographic operations in the web client happen via WASM compiled from our Rust crypto code, also subject to SRI checking. That means even a CDN-level compromise of our static assets would fail to execute on user browsers — the cryptographic boundary is enforced at the browser, not just the origin server.

Related reading: supply chain attacks on privacy software covers the broader threat class that SRI partially addresses.

Try Haven free for 15 days

Encrypted email and chat in one app. No credit card required.

Get Started →