What does an external `<script>` element's `integrity` attribute add when the Content-Security-Policy already allows that third-party host?
answer
- the allowlist answers where, not what
- content pinning, not origin pinning
- digest the bytes that actually arrived
- works on script and link elements
- mismatch becomes a network error
basics
~20 sSubresource Integrity pins the bytes rather than the origin: a conforming browser digests the fetched file and refuses it unless a hash in the integrity attribute matches. An allowlist only settles where script may come from.
solid answer
~40 sA `Content-Security-Policy` source list answers *where* a script may be fetched from; Subresource Integrity answers *which bytes* are acceptable once it arrives. The `integrity` attribute carries one or more digests of the exact file you reviewed, and a conforming browser hashes the raw response body and compares before using it. If nothing matches, the fetch becomes a network error and the script never executes — there is no degraded or unverified load. That gap matters because an allowlisted third-party host is still a third party: it can be compromised, or simply publish a new build, and a source list notices neither. It pins what arrived, never who served it. The same attribute works on a `<link>` element that pulls a stylesheet.
code
html · 8 lines<script src="https://widgets.ads-partner.test/listing-carousel.v7.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous"></script>
<link rel="stylesheet"
href="https://widgets.ads-partner.test/listing-carousel.v7.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
crossorigin="anonymous">go deeper
Remember the two questions and which mechanism answers each: a source list says where a script may come from, an integrity attribute says which bytes are acceptable. Know that a mismatch stops the script running entirely.
Explain what is actually digested — the raw response body — and why a policy that allows a host cannot notice that the host changed the file. Name the elements the attribute works on.
Show the operational trade: pinning a third-party asset means the feature vanishes when the vendor rebuilds, so you own a coordination cost, and the symptom looks like an outage. Say what you would do about unpinnable assets.
The judgment is where content pinning is worth its coordination cost across an estate of pages and vendors, and which third-party dependencies should be self-hosted from a reviewed copy rather than pinned at someone else's host.
## What an allowlist settles, and what it leaves open A `Content-Security-Policy` source list answers exactly one question: **which origins may this document fetch script from**. Once `script-src` names the widget vendor's host, every file that host serves is permitted — the build you reviewed, tomorrow's build, and anything an attacker who reaches that host publishes in between. An allowlisted third party is still a third party. On a long-lived classified-ads marketplace that embeds a shared listings widget from a host it does not control, the policy is the wrong instrument for the risk that actually matters: not *where* the bytes came from, but *which* bytes arrived. **Subresource Integrity** answers that second question. On an external `<script>` or `<link>` element you add an `integrity` content attribute holding one or more digests of the exact file you tested. The fetch proceeds normally; before the response is used, a conforming browser computes the digest of the **raw response body** and compares it against the metadata. A match lets the script execute or the stylesheet apply. No match, and the load fails. ## What goes in the attribute - The value is a whitespace-separated list of **hash-with-options** entries. - Each entry is a **hash-expression**: a `hash-algorithm` token, a literal `-`, and a `base64-value` encoded per `RFC 4648`. - The valid algorithm tokens are `sha256`, `sha384` and `sha512`. - An **option-expression** may follow a `?`. Options are reserved, and one a client does not recognise is ignored rather than treated as an error. - The digest covers the bytes that arrived — not source you wrote, not a normalised or minified form, and not the URL. One disambiguation is worth making early, because both things get called "the hash" and they are not the same. A CSP `hash-source` written inside a `script-src` — a token shaped like `'sha256-…'` — is computed over the UTF-8 encoding of an **inline** element's text. An `integrity` value is computed over the fetched bytes of an **external** resource. Different inputs, different values, even for content that looks identical. ## What a mismatch produces A mismatch is a **network error**, not a warning and not an unverified load. The element takes its error path: a `<script>` does not execute, a stylesheet is not applied, and nothing partially-verified reaches the document. That is the point — a pinned file either is the file you approved or is absent — and it is also the trade you are accepting. Pinning a third-party asset means the feature disappears the moment the vendor ships a new build, so the page has to survive the widget not being there. The diagnostic trap follows from the same fact: because the failure is shaped like a failed fetch, a widget that vanishes the day the vendor rebuilds looks exactly like the vendor's host being down. ## Where the guarantee can be attached | the question | what answers it | |---|---| | where may this script come from | the policy's source list | | which bytes are acceptable | the `integrity` attribute | | may the response be read at all | the CORS grant the `crossorigin` attribute asks for | The attribute is not limited to `<script src>`. A `<link>` element that pulls a stylesheet takes it, as does a `<link>` that preloads a resource, and integrity metadata can also travel on a `Link` **response header** alongside `rel=preload` — so the requirement can be attached to a preloaded file before any markup naming it has been parsed. ## What it does not give you 1. **It does not say who served the bytes.** It says what the bytes are. A file delivered by an entirely different host passes if its digest matches, and a file from the legitimate host fails if it does not. Authenticating the peer is the connection's job. 2. **It does not make a file safe.** A digest of a compromised build is honoured just as faithfully as a digest of a good one. The attribute pins content to a decision you made; it does not make the decision. 3. **It cannot pin what you cannot predict.** Anything generated per request, or served from a URL the vendor mutates in place, has no stable digest to pin. 4. **It is opt-in, element by element.** One `<script>` tag that ships without the attribute quietly reverts that subresource to origin-only trust, and the page looks perfectly healthy either way. It is also a different guarantee from the integrity fields in a dependency manifest. Those are a **build-time** property of a resolved dependency graph, verified by the installer before code reaches your artifact. This one is verified by the client at **fetch time**, on a file that was never in your build at all — which is exactly why a third-party widget needs it.
- Can the guarantee be attached anywhere other than an element in the markup?Yes. Besides `<script src>` and `<link>` elements, integrity metadata can be carried on a `Link` response header alongside `rel=preload`, which attaches the requirement to the preloaded file before any markup referring to it has been parsed.
- If the vendor's host is compromised, does a matching digest still protect the page?For the pinned file, yes: the attacker's replacement hashes to something else, so it becomes a network error and never runs. It protects nothing the attacker reaches by another path — an unpinned tag on the same page, or a request the widget itself makes at runtime.
- Does a passing integrity check mean the file is trustworthy?No. It means the bytes match a digest you chose. If the build you pinned was already malicious, or the digest was copied from whatever the host happened to be serving that day, the check faithfully enforces a bad decision.
A gate list says which courier firm may deliver; a tamper-evident seal says whether this is the parcel that was promised. A parcel whose seal does not match is refused at the door, and the seal vouches for nobody who carried it.
saying these in an interview costs you the question
- Thinks the integrity attribute encrypts or signs the file
- Believes an allowlisted host cannot serve a changed script
- Says a mismatched script loads in a degraded or unverified form
- Assumes a matching digest proves who served the bytes
- Treats integrity as a replacement for a Content-Security-Policy