A CDN serves the same file to millions of sites, which makes it an attractive target: alter one file and every site that loads it runs the attacker's code. Subresource Integrity (SRI) pins the exact bytes you tested. The integrity attribute holds a base64-encoded hash prefixed with its algorithm (sha256-, sha384- or sha512-). The browser hashes the downloaded file and compares; on a mismatch the response becomes a network error, so the script never runs and the stylesheet never applies. These are the published hashes for Bootstrap 5.3.8 2,007 :
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css"
integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB"
crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"
integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI"
crossorigin="anonymous"></script>integrity works on <script> and on <link> with rel="stylesheet", preload or modulepreload. A cross-origin file must be fetched in CORS mode, so crossorigin="anonymous" is required and the CDN must send Access-Control-Allow-Origin (crossorigin). To compute a hash yourself, pipe the file through openssl dgst -sha384 -binary | openssl base64 -A and prefix the result with sha384-; for the Bootstrap CSS file it prints exactly the value above. srihash.org (https://www.srihash.org/) does the same in the browser.
The demo loads the real stylesheet with its correct hash and the real bundle with one character of the hash changed, which is exactly what the browser sees when a file has been altered:
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css"
integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB"
crossorigin="anonymous">
<div class="p-3">
<div class="alert alert-success py-2 mb-2">CSS hash matched: Bootstrap styles applied.</div>
<div class="alert alert-danger py-2 mb-0" id="js">Waiting for the script...</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"
integrity="sha384-XKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI"
crossorigin="anonymous"
onerror="js.textContent = 'JS hash mismatch: blocked. typeof bootstrap: ' + typeof bootstrap">
</script>
Two newer mechanisms extend SRI. Import maps accept an "integrity" object mapping module URLs to hashes, covering modules loaded by import statements that have no tag. The Integrity-Policy: blocked-destinations=(script) response header blocks every script lacking integrity metadata; it works in Chrome 138 1 , Firefox 145 555 and Safari 26 10 but is not yet Baseline, so start with Integrity-Policy-Report-Only.
SRI suits only files that never change: load pinned URLs such as bootstrap@5.3.8, never a "latest" alias. It cannot protect dynamic services either. The polyfill.io domain changed owners in February 2024, and in June it was found injecting redirects to betting sites into more than 100,000 websites; because the service generated a different bundle per browser, nobody could have pinned it with a hash. Self-host critical scripts, or load immutable, versioned files and hash them.