securing CDN scripts against silent tampering
Anyone embedding JavaScript or CSS from an external CDN is trusting that the provider will never be compromised. Subresource Integrity replaces that blind trust with a cryptographic checksum the browser verifies against the actual file content before execution, making silent supply chain attacks technically visible.
Table of Contents
Eleven sections, from risk framing through hash generation to automated build pipeline integration.
- 1. Why embedding a CDN is a supply chain risk
- 2. How the integrity attribute works technically
- 3. Generating hashes: openssl, npm tools and build integration
- 4. The crossorigin attribute and CORS prerequisites
- 5. Versioning: why SRI prevents auto-updates
- 6. Fallback strategies for failed integrity checks
- 7. Maintaining SRI automatically in build pipelines
- 8. Common mistakes when introducing SRI
- 9. SRI compared to other CDN protections
- 10. Summary
- 11. FAQ
1. Why embedding a CDN is a supply chain risk
Subresource Integrity solves a problem many teams underestimate: as soon as an application embeds a script from a foreign CDN, for example <script src="https://cdn.example.com/lib.js">, the operator of that CDN has full control over the code that gets executed. If the CDN is compromised, whether through a stolen API key, a vulnerability in the vendor's build system, or a malicious employee, every website embedding that script can suddenly execute tampered code, without a single line of its own source code being changed.
Such incidents are not theoretical: several large supply chain attacks in recent years ran exactly through this path, a compromised npm package or a tampered CDN script infected thousands of downstream websites simultaneously. Subresource Integrity counters this risk with a simple principle: the browser still loads the script from the CDN, but only executes it if the computed hash exactly matches the hash stated in the HTML. Any tampering, even a single altered byte, results in an immediate block.
For shop operators this risk is especially relevant, because checkout pages frequently load several third party scripts at once, for example for payment processing, fraud detection and tracking. A single compromised CDN script in that chain is enough to capture every customer's card data, without the shop operator ever having changed a single line of their own code.
2. How the integrity attribute works technically
For developers working with Subresource Integrity for the first time, it is worth a brief look at the underlying specification from the W3C Web Application Security Working Group, which authoritatively defines the exact verification algorithm and supported hash methods.
The integrity attribute is set on script and link elements and contains a base64 encoded cryptographic hash of the expected file, prefixed with the algorithm used. Supported are sha256, sha384 and sha512, with sha384 being the common standard in practice, because it offers a good tradeoff between cryptographic strength and hash length in the HTML.
Once the browser has downloaded the resource, it computes the hash itself over the received byte stream and compares it against the value in the integrity attribute. If they match, the script executes or the stylesheet applies. If they diverge, the browser discards the resource entirely and reports an error in the console, without even a fragment of the tampered code being executed. This mechanism works entirely client side and requires no server configuration on the CDN's part.
<!-- SRI-protected CDN script with crossorigin and integrity hash -->
<script
src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"
integrity="sha384-6e6dNn0hPzP0X8qb9y1L8qBk8G3vJm3lJt2R6t9E5Qx1Zz3lQxq2s9jY7v3H5rV1"
crossorigin="anonymous"
></script>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/normalize.css@8.0.1/normalize.min.css"
integrity="sha384-4d3P6q2X9y5t7Z8k1J0nQ2wR5o3Vc7Tt6y8B9x2K1L4M0N7P3q6R9t2Y5w8Z1a3"
crossorigin="anonymous"
>
3. Generating hashes: openssl, npm tools and build integration
The correct hash can be computed with standard tools via openssl, by first downloading the file with curl and then hashing it. It is important that the hash is computed over exactly the bytes the browser actually receives, including any compression or encoding differences some CDNs deliver depending on the Accept-Encoding header.
For npm based projects, specialized packages like ssri exist that generate the hash directly from locally installed node_modules files, ensuring the version in the hash exactly matches the version in the package.json lockfile. This automation matters because a manually computed hash goes stale quickly once a package update lands, and a forgotten hash update means the application completely breaks after every dependency update.
# Compute SRI hash manually with openssl and base64
curl -s https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js \
| openssl dgst -sha384 -binary \
| openssl base64 -A
# Or with the ssri npm package against a local file
npx ssri generate node_modules/chart.js/dist/chart.umd.min.js --algorithms sha384
# Verify an existing hash against the currently published CDN file
curl -s https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js \
| openssl dgst -sha384 -binary | openssl base64 -A
A recurring verification step in the CI pipeline, checking the hash stored in the HTML against a freshly downloaded copy of the file, reliably reveals if a CDN provider has unexpectedly swapped the content behind a versioned URL, something that should never happen but occasionally does in practice.
4. The crossorigin attribute and CORS prerequisites
A frequently overlooked building block of Subresource Integrity is the crossorigin attribute, which must be set whenever the resource is loaded from a different origin. Without crossorigin="anonymous", the browser denies JavaScript access to the response data needed for hash computation, even if the server is technically reachable, and the integrity check fails outright.
The CDN server must therefore deliver matching CORS headers such as Access-Control-Allow-Origin: *, otherwise the browser already refuses to load the resource in combination with crossorigin. Most large CDNs like jsDelivr, cdnjs or unpkg deliver these headers by default, but a self hosted internal CDN must explicitly configure this, otherwise Subresource Integrity does not work for internal resources.
5. Versioning: why SRI prevents auto-updates
A central effect of Subresource Integrity, often misunderstood as a downside, is that it completely blocks automatic updates through URLs without a fixed version, for example cdn.example.com/lib/latest/app.js. Since the hash matches exactly one file version, every new version of the CDN script requires an explicit new hash in the HTML. That is intentional: without this binding, the CDN operator could swap the file behind the same URL at any time, and Subresource Integrity would only protect the first version.
For projects that rely on fast security updates of third party libraries, that means an additional maintenance step: every update of a CDN dependency requires recomputing the hash in the same commit. This coupling forces a deliberate, reviewable update decision instead of a silent, uncontrolled swap in the background, which significantly improves the traceability of third party code over time.
6. Fallback strategies for failed integrity checks
If the integrity check fails, the browser does not load the resource, but it also runs no automatic fallback, the application itself must catch that via the onerror event. A robust strategy loads a locally hosted copy of the same version when a CDN script fails, so the application does not completely break just because an external CDN is temporarily unreachable or compromised.
It is important not to view the fallback purely as an availability measure, but also as a safety net: a locally hosted script coming from the same trusted build pipeline is not subject to the same supply chain risks as the external CDN. For critical applications, such as checkout flows in an online shop, a local fallback with no external dependency is often the more robust baseline decision, with CDN embedding used only as a performance optimization through caching.
// Fallback to a locally hosted copy if the SRI-protected CDN script fails to load
function loadWithFallback(cdnSrc, cdnIntegrity, localSrc) {
const script = document.createElement('script');
script.src = cdnSrc;
script.integrity = cdnIntegrity;
script.crossOrigin = 'anonymous';
script.onerror = () => {
console.warn('CDN script blocked or unreachable, loading local fallback');
const fallback = document.createElement('script');
fallback.src = localSrc; // no integrity needed for same-origin trusted build
document.head.appendChild(fallback);
};
document.head.appendChild(script);
}
// Usage: checkout page loads a payment SDK with automatic local fallback
loadWithFallback(
'https://js.paymentprovider.example/v3/sdk.js',
'sha384-9y1P0X8qb9y1L8qBk8G3vJm3lJt2R6t9E5Qx1Zz3lQxq2s9jY7v3H5rV1Z8k1J0n',
'/assets/vendor/payment-sdk-fallback.js'
);
For checkout critical scripts, it is advisable to place the fallback call as early as possible in the load process and only start the rest of the checkout flow after either the CDN script or the local copy has loaded successfully. A timeout of a few seconds prevents customers from waiting unnecessarily long for a response when a CDN is slow but not fully down.
7. Maintaining SRI automatically in build pipelines
Manually maintained hashes go stale quickly in practice, so productive setups integrate hash computation directly into the build process. Webpack plugins like webpack-subresource-integrity automatically compute hashes for every bundled asset and write them into the generated HTML files, with no manual intervention needed on every deployment.
For projects that still reference external CDN scripts, a dedicated build script that automatically pulls new hashes on every dependency update and opens a pull request with the change is worth the investment. This automation prevents the most common practical mistake with Subresource Integrity: a stale hash that breaks the application completely after a CDN side update, because nobody refreshed the hash in time.
8. Common mistakes when introducing SRI
The most common mistake is forgetting the crossorigin attribute, which makes the integrity check fail outright regardless of a correct hash. A second mistake is using latest or similarly unversioned URLs together with a static hash, which is guaranteed to fail after the next CDN side update, as soon as the file content behind the same URL changes.
A third, more subtle mistake concerns computing the hash over a compressed rather than the uncompressed transfer. Some CDNs deliver differently compressed variants depending on Accept-Encoding, but the hash must exactly match the byte stream the given client actually receives. In practice it is advisable to pull the hash directly from the package's official distribution, for example via ssri against the local node_modules file, rather than computing it manually against a live fetched CDN URL.
9. SRI compared to other CDN protections
Alongside Subresource Integrity, other approaches exist to limit CDN risk. The following table compares them directly.
| Measure | Protects against | Effort | Limits |
|---|---|---|---|
| Subresource Integrity | Tampered file content at the CDN | Hash maintenance per version | No protection against an entirely new, malicious URL |
| Content Security Policy (script-src) | Scripts from disallowed domains | Header maintenance, domain list | Does not protect against tampering of the allowed domain itself |
| Self hosted copy | Every external CDN risk entirely | Own hosting and update responsibility | No more automatic CDN caching |
In practice, combining Subresource Integrity with a restrictive Content Security Policy is the most robust solution: the Content Security Policy limits allowed domains, Subresource Integrity ensures that even an allowed domain cannot serve tampered content without the browser noticing.
Mironsoft
Supply chain security, CDN hardening and build pipeline automation
Secure CDN scripts against silent tampering?
We review existing CDN embeds, add missing integrity hashes and automate hash maintenance directly in your build pipeline, including a fallback strategy for critical application parts.
CDN audit
Analysis of all externally embedded scripts and stylesheets
Hash automation
Build pipeline integration for automatic hash updates
Fallback design
Local fallback strategy for critical checkout and login flows
For Magento and Hyvä shops with several externally embedded payment and tracking scripts, a central module that maintains all SRI hashes in a single place in the layout XML, rather than scattering them across various templates, is worth the investment. It simplifies later audits and makes visible which external dependencies are actually active in the checkout.
10. Summary
Subresource Integrity closes a gap many teams only take seriously after a concrete incident: blind trust in external CDN providers. Through the integrity attribute with a SHA-384 hash and the mandatory crossorigin attribute, the browser ensures that only exactly the expected file content is executed, any tampering at the CDN becomes technically visible and gets blocked.
The version bound nature of Subresource Integrity forces deliberate, reviewable updates instead of silent background changes, justifying the additional maintenance effort. Build pipeline integration through tools like webpack-subresource-integrity or ssri reduces that effort to a minimum and makes Subresource Integrity one of the few security measures that can be almost fully automated without slowing down development speed.
Subresource Integrity in Practice — The Essentials at a Glance
Four building blocks that add up to resilient protection against tampered CDN scripts in production.
integrity attribute
A SHA-384 hash in the integrity attribute blocks tampered files before they execute.
crossorigin required
Without crossorigin="anonymous" the integrity check fails outright for a foreign origin.
Versioning
SRI prevents auto-updates via unversioned URLs, every new version needs a new hash.
Automation
Build pipeline tools like webpack-subresource-integrity or ssri maintain hashes with no manual effort.