Subresource Integrity
Why it matters When you embed https://cdn.example/lib.js, you trust whoever controls that server today and every day after. If it is hacked, or the file is quietly changed, your visitors run the new code on your site. Subresource Integrity lets the browser refuse any version you did not approve.
Think of a tamper-evident seal on a medicine bottle. You check the seal before you swallow anything. If it is broken, you do not take it, whoever put it on the shelf.
Subresource Integrity (SRI): an integrity attribute on a <script> or <link rel="stylesheet"> tag holding a hash of the exact file you expect. The browser hashes what it downloads and refuses to use the file if the hashes differ.
Hash: a function that turns any data into a short fixed-size fingerprint. The same bytes always give the same fingerprint; change one byte and the fingerprint changes completely. SHA-384 is the hash most often used for SRI.
What the tag looks like
<script src="https://cdn.example/lib.js"
integrity="sha384-iht3RcshTShmtRmXjSLQ8CjUTiVO41jHRJzgK44j1lSJHvfC4YAeuY4ZOoMtzhi5"
crossorigin="anonymous"></script>The value is the algorithm name, a dash, then the hash written in Base64 (a way of writing bytes using letters, digits, + and /). The crossorigin="anonymous" part is needed for files from another site: the browser fetches them without cookies and the server must allow it with an Access-Control-Allow-Origin header, otherwise the check cannot run and the file is blocked. SRI works in all major browsers (checked October 2026).
Computing one in Python
import base64, hashlib
data = b"console.log('hi')"
digest = hashlib.sha384(data).digest() # 48 raw bytes
print("sha384-" + base64.b64encode(digest).decode("ascii"))hashlib.new(name, data) does the same for any algorithm given by name, such as "sha256" or "sha512". Note .digest() (raw bytes, what SRI wants) versus .hexdigest() (hex text, which would be wrong here).
How the browser checks it
- Split the attribute on spaces. You may list several hashes, for example while switching file versions.
- Keep only tokens with an algorithm it knows:
sha256,sha384,sha512. Ignore the rest. (A token may end in?options; drop that part.) - If nothing usable is left, it does no check at all and uses the file. A typo such as
sha385-...therefore silently switches protection off. Always test that a wrong hash really blocks the file. - Otherwise, take the strongest algorithm present (sha512 beats sha384 beats sha256) and look only at tokens with that algorithm. If any of them matches the file's hash, the file is used; if none does, it is blocked.
A common misconception: "SRI keeps the file private". It does not hide anything, and the third party still sees the request. SRI only guarantees the code is the code you approved. It also cannot work for files that change all the time, like an ad script; for those, the honest answer is often not to embed them.
In other languages
# Shell (OpenSSL)
openssl dgst -sha384 -binary lib.js | openssl base64 -A// JavaScript (Node)
const crypto = require("crypto");
const sri = "sha384-" + crypto.createHash("sha384").update(fs.readFileSync("lib.js")).digest("base64");// Go
sum := sha512.Sum384(data)
sri := "sha384-" + base64.StdEncoding.EncodeToString(sum[:])// PHP
$sri = 'sha384-' . base64_encode(hash('sha384', $data, true));// C#
var sri = "sha384-" + Convert.ToBase64String(SHA384.HashData(data));Bundlers can add SRI for you (for example the webpack-subresource-integrity plugin).
Recap
- SRI pins an embedded script or style sheet to the exact bytes you approved.
- The value is
algorithm-Base64(hash), and cross-site files also needcrossorigin="anonymous". - The browser uses only the strongest algorithm listed, and an attribute with nothing usable means no check.
Write three functions:
sri_hash(data, algorithm="sha384"):datais bytes. Return the SRI value: the algorithm name, a dash, and the Base64 of the raw digest (usehashlib.new(algorithm, data).digest()andbase64.b64encode).verify_sri(data, integrity): checkdatathe way a browser does. Splitintegrityon whitespace; keep tokens whose part before the first-issha256,sha384orsha512and whose value is not empty, dropping anything from a?onwards. If none are kept, returnTrue(no check). Otherwise use only the strongest algorithm present and returnTrueif any of its values matches the data's hash in that algorithm, elseFalse.script_tag(url, data): return exactly<script src="URL" integrity="VALUE" crossorigin="anonymous"></script>with the sha384 value fordata.
This lesson is locked
Lessons open one at a time. Finish the previous lesson to unlock this one.