Third-party JavaScript security: how supply-chain attacks work

Third-party scripts run with full DOM access on your site. One compromised CDN or npm package can skim cards from every visitor. Here's exactly how it happens.

JavaScript supply chain attacks — how third-party scripts get compromised

When your site loads a script from an external CDN, that script executes with exactly the same trust as your own code. It can read form fields, intercept keystrokes, exfiltrate cookies, and silently redirect users. You don't audit it on every page load — and that's the attack surface.

How third-party JavaScript executes with full page access

Browsers apply the Same-Origin Policy to data requests, not to scripts. A <script src="https://cdn.example.com/lib.js"> tag gives that script full access to document, window, all DOM nodes, and every in-page credential. There is no sandbox. The script can call fetch() to any origin with no restriction — including to an attacker-controlled server.

This is why Magecart-style skimmers work: a few lines of JavaScript attached to a payment form can silently POST card data to a remote endpoint while the legitimate transaction completes normally. The user sees no error; the merchant sees no anomaly.

The CDN compromise pattern

Supply-chain attacks on JavaScript follow a predictable kill chain:

  • Acquire control — attacker compromises a CDN account, npm registry package, or domain name (via expiry, account takeover, or acquisition).
  • Inject payload — malicious code is inserted into a widely-used script file. The file hash changes; the URL stays the same.
  • Propagate silently — every site that loads that URL now serves the attacker's code. Sites that pinned a version tag pointing to a mutable file are equally affected.
  • Harvest — keyloggers, form interceptors, or session token exfiltration run in the background. Victims may not discover the breach for weeks.

The polyfill.io compromise (June 2024)

In February 2024, the polyfill.io domain and GitHub repository were acquired by Funnull, a Chinese CDN company. By June 2024, Funnull was serving malicious JavaScript through cdn.polyfill.io. The payload specifically targeted mobile users — it checked the User-Agent header and only activated on Android and iOS devices to evade desktop-based security researchers.

The injected code redirected mobile visitors to scam sites and sports-betting pages via fake Google Analytics links. Approximately 100,000+ websites that included polyfill.io were affected — the majority of which had no idea they were distributing malware. Sites ranging from enterprise portals to small e-commerce stores were silently compromised because they loaded a convenience library they had never audited.

The incident was discovered and publicly disclosed by security researcher Sansec and confirmed by Cloudflare and Fastly, both of which quickly offered alternative CDN endpoints for polyfill.

Detecting changed script hashes

The most direct detection method is tracking the cryptographic hash of each external script on every page load. If the hash changes without a corresponding release in the vendor's changelog, something has been modified.

Fetch a script and compute its SHA-256:

curl -s https://cdn.example.com/lib.js | openssl dgst -sha256 -binary | openssl base64

Compare the output against your known-good baseline. Automate this check on a schedule — not just at deploy time — because CDN files can change independently of your deployment pipeline.

Subresource Integrity (SRI)

SRI is the browser-native mechanism for pinning external scripts to a specific hash:

<script src="https://cdn.example.com/lib.js"
  integrity="sha384-abc123..."
  crossorigin="anonymous"></script>

If the fetched file's hash doesn't match the integrity attribute, the browser refuses to execute it. The protection is real — but only if you:

  • Populate the integrity attribute with the hash of a specific, known-good version.
  • Keep the hash updated when you intentionally upgrade the library.
  • Ensure the CDN serves the file with Access-Control-Allow-Origin: * (required for crossorigin attribute to work).
  • Don't load the same library from a second, un-pinned URL — attackers inject via the path of least resistance.

Content Security Policy (CSP) as a second layer

CSP restricts which origins can supply scripts. A strict policy blocks unapproved script sources entirely:

Content-Security-Policy: script-src 'self' https://cdn.example.com; object-src 'none'; base-uri 'self';

CSP doesn't protect against a compromise of an explicitly allowed origin (like polyfill.io was), but it prevents new unauthorized script origins from being injected. Combined with SRI, you get defense-in-depth: CSP limits which CDNs are trusted, SRI pins the exact file at each trusted CDN.

Use Content-Security-Policy-Report-Only first to audit violations before enforcing.

Remediation steps

  • Audit every <script src> tag across your site. Include tags injected by tag managers — these are often overlooked.
  • Add SRI hashes to all third-party scripts. Use srihash.org or compute locally.
  • Replace polyfill.io with cdnjs.cloudflare.com, unpkg.com, or bundle polyfills locally.
  • Deploy a strict CSP. Start with report-only mode; review the violations report for two weeks before switching to enforced.
  • Subscribe to security advisories for every npm package and CDN library your site loads.
  • Monitor script hashes on a schedule, not just at deploy time.

Common mistakes

  • Loading scripts via tag manager without SRI — tag managers let marketers add any script without engineering review. Enforce SRI at the CDN layer or audit tag manager configurations regularly.
  • Using version-pinned URLs that point to mutable files — a URL like ?v=3.2.1 in a query string doesn't guarantee the file is immutable. Pin the hash, not the version string.
  • Assuming CSP alone is sufficient — if an approved CDN is compromised, CSP won't help. You need SRI and monitoring too.
  • Not testing SRI in staging — a wrong hash silently breaks functionality. Test before production.

Orb44's Watch feature tracks third-party script fingerprints across your pages and alerts you when a script hash changes between scans — catching CDN-level compromises that your deployment pipeline never touches.

FAQ

Does SRI protect against all supply-chain attacks?

No. SRI protects against the case where a CDN file changes without your knowledge. It doesn't protect against attacks where you voluntarily upgrade to a malicious version, or where the attacker modifies files you haven't pinned. Defense-in-depth — SRI + CSP + monitoring — is required.

Should I self-host all third-party JavaScript?

Self-hosting removes CDN supply-chain risk but adds maintenance burden: you must manually track upstream security patches. For actively maintained libraries (React, jQuery, etc.), self-hosting is reasonable. For convenience utilities (polyfills, analytics snippets), evaluate whether you need them at all.

How often do supply-chain attacks happen?

Documented incidents now occur multiple times per year. The 2022 node-ipc sabotage, the 2023 @ledgerhq/connect-kit Magecart inject, and the 2024 polyfill.io compromise are high-profile examples. Smaller incidents affecting individual npm packages happen weekly at this point.

Orb44 Journal · all posts