auditing-third-party-script-and-sri-trust · git:20260901.f8395d1 · 2026-09-01 · sha256 2038a52a15de7b2d
auditing-third-party-script-and-sri-trust git:20260901.f8395d1B
Immutable. This exact content is served forever at /api/v1/blob/2038a52a15de7b2d.
--- name: auditing-third-party-script-and-sri-trust description: >- Audit a web application for trust placed in third-party scripts it loads into its own page: an external script tag with no Subresource Integrity hash so a compromised CDN or vendor serves altered code that runs with full page privileges, a tag manager or analytics loader that injects further scripts the site never reviews, a script served over a mutable URL or wildcard source that can be swapped, and a Content-Security- Policy that is missing or permissive enough to allow arbitrary external script. Covers browser front ends, marketing and analytics tags, payment and widget embeds, and any page that loads JavaScript it did not author from another origin. Use when a page runs third-party script in its own security context and the integrity of that code is the boundary. The compromised or swapped third-party script is the source, the full-privilege execution in the page (data theft, skimming, defacement) is the sink, and the missing integrity pin or permissive script policy is the bug. license: MIT --- # Auditing third-party script and SRI trust: a script tag is code you did not write running as you Every external script a page loads runs in that page's security context: it can read the DOM, cookies accessible to script, form fields, and tokens, and it can send them anywhere. So a third-party script is not a dependency the way a backend library is; it is code you did not write, delivered by a party you do not control, executing with your page's full privileges on your users. The risk is supply-chain: the vendor's CDN is compromised, the vendor ships a malicious update, or the script URL is swapped, and altered code runs on every visitor. A form-skimming attack that steals payment details is exactly this, a legitimate analytics or widget script replaced with one that also reads the checkout form. The defenses are integrity pinning (a Subresource Integrity hash so the browser refuses altered code) and a Content-Security-Policy that constrains which origins may serve script at all, and a tag manager that injects further scripts undoes both by loading code nobody reviewed. The audit inventories every third-party script, checks whether its integrity is pinned and its origin constrained, and treats any unpinned externally served script as attacker-controllable. You audit this by listing what the page loads and asking, for each, what happens if that code is swapped. ## When to use - A web page loads JavaScript from another origin: a CDN, an analytics or marketing tag, a payment or chat widget, a tag manager. - External script tags may lack Subresource Integrity hashes, or scripts may be served over mutable or wildcard URLs. - A tag manager or loader may inject further scripts, and the Content-Security-Policy may be missing or permissive. ## Scope check Audit third-party script trust only on sites and properties you own or are authorized to assess. Reviewing what a page loads is passive, but testing swap or injection scenarios touches real third-party relationships, so stay within your own properties and test deployments and never tamper with a vendor's delivery or another site's scripts. If you can't name the authorization, stop. ## The loop 1. **Establish the intended set of third-party code first.** Inventory every external script the page loads, who serves it, and why, including scripts injected by tag managers and loaders. Name what each is allowed to do and what would happen if it were swapped. This is the false-positive killer: a page that loads a known, minimal set of scripts, each pinned with Subresource Integrity, from origins a strict CSP allows, with no uncontrolled tag-manager injection, has bounded its third-party trust. Name the intended set, then check the controls. 2. **Check integrity pinning (SRI).** For each externally served script, confirm it carries a Subresource Integrity hash and `crossorigin` so the browser refuses to run code that does not match. An external script with no SRI hash runs whatever the CDN or vendor serves, so a compromised or updated source executes altered code with no barrier. 3. **Assess the Content-Security-Policy for script.** Determine which origins the CSP allows to serve script and whether it permits inline script, `unsafe-eval`, or wildcard sources. A missing CSP, or one permissive enough to allow arbitrary external script, means nothing constrains what code the page will run. Confirm the policy is a real allowlist, not a formality. 4. **Trace tag managers and script injectors.** Follow any tag manager, consent manager, or loader that injects further scripts at runtime. These load code the site never directly reviewed and often cannot pin, so they are a hole straight through SRI and CSP. Confirm injected scripts are governed (reviewed, pinned where possible, and CSP-constrained) rather than trusted wholesale. 5. **Check URL mutability and origin control.** Determine whether each script URL is versioned and immutable or points at a mutable "latest" path, and whether the serving origin is one the vendor could repoint. A script at a mutable URL or a wildcard origin can be swapped without the page changing at all, defeating a review done once. 6. **Confirm and record.** Confirm by demonstrating (on your own test deployment) that an unpinned or permissively allowed script would execute swapped code with full page privileges, reading form fields or tokens and exfiltrating them, without tampering with a real vendor. Kill the lead if every third-party script is integrity-pinned, served from a strict-CSP-allowed immutable origin, and no tag manager injects ungoverned code. Record the compromised or swapped script, the full-privilege execution in the page, and the missing integrity pin or permissive script policy. ## Where third-party script trust leaks - **No Subresource Integrity.** An external script with no SRI hash runs whatever its CDN or vendor serves, so a compromise or update executes altered code unchallenged. - **Missing or permissive CSP.** No script allowlist, or one that permits wildcard origins, inline script, or `unsafe-eval`, leaves nothing constraining what code the page runs. - **Tag-manager injection.** A tag or consent manager that injects further scripts at runtime loads unreviewed, often unpinnable code straight past SRI and CSP. - **Mutable script URLs.** A script at a "latest" or unversioned URL can be swapped without any change to the page, defeating a one-time review. - **Wildcard or repointable origins.** A CSP or tag that allows a broad origin lets a vendor (or an attacker who compromises it) serve different code from an allowed source. ## Worked example (a confirm and a kill) > **Confirm.** A checkout page loads an analytics script from a vendor CDN with a plain `<script src>` tag, no > SRI hash, and a CSP that allows the vendor's wildcard origin. On a test deployment, replacing the served > script with one that also reads the card-number field and posts it to an external URL runs unchallenged, > because nothing pins the code and the origin is broadly allowed, the shape of a form-skimming compromise. > **Confirmed** third-party script skimming exposure via missing SRI and permissive CSP, `high`, remediation = > add a Subresource Integrity hash and `crossorigin` to the script, pin it to an immutable versioned URL, and > tighten the CSP to the exact origins and paths needed with no wildcard or inline script. > > **Kill.** Every third-party script carries an SRI hash and `crossorigin`, is loaded from an immutable > versioned URL on an origin a strict CSP allows by exact host, the CSP forbids inline script and `unsafe-eval`, > and no tag manager injects ungoverned code. A swapped script fails the integrity check and never runs; an > unlisted origin is blocked by the CSP. **Killed**, `kill_reason` = "all third-party script is integrity-pinned > from strict-CSP-allowed immutable origins with no ungoverned injection; swapped or altered code does not > execute." ## Rationalizations to reject - *"It is a reputable vendor."* → Reputable CDNs and vendors are exactly the supply-chain targets; pin the code with SRI so a compromise of the reputable party still cannot run altered script. - *"We reviewed the script."* → A review is a point in time; an unpinned or mutable-URL script can be swapped after review with no change you would notice, so pin and version it. - *"We have a CSP."* → Confirm it is a real allowlist without wildcards, inline script, or `unsafe-eval`; a permissive CSP allows the attacker's script as readily as the vendor's. - *"The tag manager handles that."* → A tag manager injects code you did not review and often cannot pin; it is a hole through your controls, not a control. - *"It is just analytics."* → Analytics runs with full page privileges and can read the same forms and tokens as any script; there is no low-privilege third-party script. ## Executing this in practice You need the full inventory of external scripts (including tag-manager-injected ones), each script's SRI hash and `crossorigin` state, the CSP's script-source directives, and whether each URL is immutable and versioned. List what the page loads, check each for an integrity pin and a strict allowed origin, and follow every injector. Reading the page source and CSP shows the intended trust; a swapped script that runs on a test deployment shows what the controls fail to stop. ## Related - `auditing-clickjacking-and-ui-redressing` - the other front-end trust boundary enforced by response headers; CSP and framing controls are audited from the same page configuration. - `hunting-supply-chain-risks` - the build-time supply chain; third-party script is the runtime, in-browser supply chain of the same kind of trust. - `mapping-attack-surface` - use it to enumerate every origin and script a page loads before checking each one's integrity and policy. - `hunting-wallet-drainer-and-dapp-approval-abuse` - a compromised dApp script is how a drainer swaps a spender or prompt; front-end integrity and approval abuse meet in the page. - [FINDING-SCHEMA.md](../../FINDING-SCHEMA.md) - source = the compromised or swapped third-party script, sink = the full-privilege execution in the page, evidence = the missing integrity pin or permissive script policy.