Skip to content

9.4 Client-Side JavaScript and CDN Supply Chains

The previous sections examined supply chains that operate during development and build—npm packages bundled into applications, mobile SDKs compiled into apps. But the web presents a distinct paradigm: JavaScript loaded directly into users' browsers at runtime from external servers. Every time a page loads, browsers fetch scripts from multiple origins, executing code that can access the page's DOM, user data, and in some cases, cryptographic keys or payment information.

This client-side supply chain operates in real-time, with trust established at the moment of execution rather than during development. A CDN compromise or malicious script injection affects users immediately, without any deployment by the site operator.

The Third-Party Script Landscape

Modern websites load extensive third-party JavaScript. HTTP Archive data (2024) reveals the scale:

  • The median desktop page makes 11 first-party and 10 third-party JavaScript requests
  • Popular sites often load from 15-20+ third-party sources
  • Third-party JavaScript accounts for 45% of script bytes on average1
  • E-commerce sites commonly exceed 30 third-party scripts

Common Third-Party Script Categories:

  • Analytics: Google Analytics, Adobe Analytics, Mixpanel, Hotjar
  • Advertising: Google Ads, Facebook Pixel, ad network scripts
  • Tag managers: Google Tag Manager, Tealium, Segment
  • A/B testing: Optimizely, VWO, Google Optimize
  • Social buttons: Facebook Like, Twitter share, LinkedIn widgets
  • Chat widgets: Intercom, Drift, Zendesk
  • Payment processors: Stripe.js, PayPal buttons
  • Customer data platforms: Various tracking and personalization tools
  • Consent management: Cookie consent popups
  • Libraries via CDN: jQuery, Bootstrap, React from public CDNs

Each script is a dependency loaded at runtime. Unlike build-time dependencies locked to specific versions, these scripts may change on the remote server without site operators knowing.

Runtime Loading Risks

Loading scripts from external sources creates unique risks:

Dynamic Trust:

Build-time dependencies are fixed when you deploy. Runtime dependencies are trusted at every page load. If an external script changes between page loads, your site behavior changes—potentially maliciously.

No Review Opportunity:

With npm packages, you can theoretically review code before bundling. With runtime-loaded scripts, you trust whatever the external server returns at request time. There's no pre-deployment review.

Immediate Impact:

A compromised npm package requires update and deployment cycles before affecting users. A compromised runtime script affects users on the next page load—instantly, globally.

Transitive Loading:

Third-party scripts can load additional scripts. A tag manager might load dozens of subsequent scripts, each from different sources. The site operator may not know what's actually executing.

Session Context:

Runtime scripts execute with full access to the page context: DOM, cookies, session storage, form data, and any secrets present on the page. A compromised analytics script can steal everything a user enters.

Subresource Integrity (SRI)

Subresource Integrity (SRI) provides cryptographic verification for externally-loaded scripts:

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

The browser verifies the script's content matches the specified hash before execution. If an attacker modifies the script, the hash won't match, and the browser refuses to execute it.

SRI Limitations:

Despite its value, SRI has significant limitations explaining low adoption:

  • Only works for static resources: Dynamic scripts that change (tag managers, analytics) cannot use SRI
  • Breaks when scripts update: Any legitimate update requires updating the hash
  • Requires CORS headers: Scripts must include appropriate cross-origin headers
  • No protection against legitimate compromise: If you update the hash to match a compromised script, SRI doesn't help

Adoption Reality:

SRI adoption has improved but coverage remains limited according to HTTP Archive 2024 data:

  • Approximately 21-23% of pages include some form of SRI
  • However, the median percentage of scripts protected per page remains at only 3.2%
  • Dynamic script-loading patterns bypass SRI protection

SRI works well for stable libraries (jQuery, Bootstrap) loaded from CDNs but doesn't address the broader runtime supply chain.

CDN Compromises and Blast Radius

Public CDNs serve JavaScript libraries to millions of websites. Their compromise creates extraordinary blast radius.

Major JavaScript CDNs:

  • cdnjs (Cloudflare): Over 4,500 libraries, serving over 12.5% of websites
  • jsDelivr: Aggregates npm, GitHub, and custom packages
  • unpkg: Serves npm packages directly
  • Google Hosted Libraries: jQuery, Angular, and other popular libraries

Trust Model:

When you load from a public CDN, you trust:

  1. The CDN operator's security
  2. The CDN's infrastructure
  3. The underlying package registry (often npm)
  4. DNS resolution to the CDN
  5. TLS certificate issuance

A failure at any point compromises every site using that resource.

Historical CDN Concerns:

While major CDN compromises have been rare, close calls exist:

The Centralization Paradox:

Centralizing libraries on major CDNs provides security benefits (professional operation, rapid patching) but also creates single points of failure. A cdnjs compromise would affect over 12% of the web instantly.

Polyfill.io and the Trust Problem

Section 7.8 detailed the Polyfill.io attack, but its relevance to client-side supply chains deserves emphasis:

The Attack Pattern:

  1. Legitimate service established trust (Polyfill.io served polyfills)
  2. Service was acquired by unknown entity
  3. New owners injected malicious code
  4. Malicious code reached over 100,000 websites (initial reports from Sansec; later analysis by Censys identified 380,000+ hosts affected)

Why It Worked:

  • Sites included <script src="https://cdn.polyfill.io/...">
  • No SRI was possible (scripts were dynamically generated)
  • Ownership change triggered no notifications
  • Detection relied on security researchers noticing anomalies

Lessons for Client-Side Supply Chains:

  • Third-party scripts are ongoing trust relationships, not one-time decisions
  • Ownership changes in external services create supply chain risk
  • Services providing dynamic content cannot be verified with SRI

Case Study: Ledger Connect Kit Attack (2023)

In December 2023, a supply chain attack on Ledger's Connect Kit JavaScript library demonstrated how client-side compromises can target cryptocurrency assets.

Background:

Ledger produces hardware cryptocurrency wallets. The Ledger Connect Kit is a JavaScript library that enables websites (decentralized applications or "dApps") to connect with Ledger hardware wallets. It was loaded by numerous cryptocurrency applications to facilitate wallet connections.

The Attack:

On December 14, 2023, attackers compromised a former Ledger employee's npm account through a phishing attack. Using this access, they:

  1. Published malicious versions of @ledgerhq/connect-kit to npm
  2. The compromised package was loaded by dApps using the Ledger Connect Kit
  3. The malicious code injected a drainer that prompted users to sign transactions transferring assets to attacker wallets

Impact:

  • The malicious code was live for approximately 5 hours
  • Over $600,000 in cryptocurrency was stolen from users
  • Multiple prominent dApps were affected, including SushiSwap and Zapper
  • Users who connected hardware wallets and approved transactions lost funds

Technical Details:

The malicious code: - Injected fraudulent transaction requests - Made malicious prompts appear legitimate - Targeted users already interacting with cryptocurrency applications - Exploited the trusted position of wallet-connection libraries

Response:

  • Ledger identified and revoked the compromised access
  • A clean version was published within hours (Ledger states a fix was deployed 40 minutes after becoming aware)
  • CDNs cached the malicious version, extending exposure
  • Ledger announced security improvements including removing npm publish access from individual accounts

Lessons:

  1. Account security is critical: A single compromised npm account enabled the attack
  2. CDN caching extends compromise windows: Even after fixing npm, cached versions remained
  3. High-value targets attract sophisticated attacks: Cryptocurrency applications face elevated threat
  4. Runtime loading amplifies impact: Sites loading the library were compromised immediately

Client-Side vs. Build-Time Supply Chain

Understanding the distinction between client-side and build-time supply chains clarifies defensive priorities:

Aspect Build-Time Client-Side (Runtime)
When dependency is fetched During development/CI Every page load
Version control Package manager lockfiles Usually latest from CDN
Review opportunity Before deployment None (trust at load time)
Compromise propagation Requires deployment cycle Immediate
Verification mechanism Lockfile hashes SRI (limited adoption)
Scope of trust Package at locked version External server continuously

Hybrid Patterns:

Many modern applications use both patterns: - npm packages bundled at build time (build-time supply chain) - Analytics and advertising loaded at runtime (client-side supply chain) - Some libraries loaded from CDNs at runtime for caching

Understanding which dependencies fall into which category is essential for applying appropriate controls.

Content Security Policy (CSP)

Content Security Policy provides browser-enforced restrictions on script loading:

Content-Security-Policy: script-src 'self' https://trusted-cdn.example.com

This header tells browsers to only execute scripts from specified sources.

CSP for Supply Chain Security:

  • Allowlist script sources: Limit which domains can serve JavaScript
  • Block inline scripts: Prevent injection attacks from executing
  • Report violations: Receive alerts when policies are violated

Limitations:

  • Doesn't validate content: CSP says where scripts can come from, not what they contain
  • Overly broad in practice: Most CSPs allow major CDNs or use unsafe-inline
  • Breaks legitimate functionality: Strict CSP requires significant development effort
  • Third-party scripts often require relaxed policies: Analytics and advertising need broad permissions

CSP complements SRI but doesn't replace content verification.

Monitoring and Detection

Detecting client-side supply chain compromises requires different approaches than server-side monitoring:

Real User Monitoring (RUM):

Tools that observe actual browser behavior can detect anomalies: - Scripts making unexpected network requests - DOM modifications inconsistent with legitimate functionality - Error rates indicating changed script behavior

Synthetic Monitoring:

Automated browsing that records script behavior over time: - Compare script content between crawls - Alert on unexpected new scripts - Detect changes in script behavior

Script Inventorying:

Maintaining awareness of what scripts load: - Browser developer tools (Network tab) - Third-party script monitoring services (Feroot, Source Defense, Jscrambler) - Content Security Policy reports

Client-Side Protection Platforms:

Specialized tools for runtime JavaScript security: - Feroot: Third-party script monitoring and control - Source Defense: Client-side protection platform - Akamai Page Integrity Manager: JavaScript monitoring - PerimeterX Code Defender: Script behavior analysis

These tools observe script execution in production, detecting suspicious behavior that static analysis would miss.

Recommendations

For Web Developers:

  1. Audit third-party scripts. Know what's loading on your pages. Use browser developer tools to inventory scripts and their sources.

  2. Use SRI for static libraries. When loading stable libraries from CDNs, implement Subresource Integrity:

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

  3. Self-host when feasible. For critical libraries, bundle at build time or host on your own infrastructure rather than trusting external CDNs.

  4. Implement Content Security Policy. Even imperfect CSP provides defense-in-depth against unexpected script sources.

  5. Minimize third-party scripts. Each external script is a trust relationship. Remove unused scripts and consolidate where possible.

  6. Load scripts asynchronously with appropriate sandboxing. Use async or defer attributes and consider iframe sandboxing for untrusted content.

  7. Monitor for script changes. Implement monitoring that alerts when third-party script behavior changes.

For Security Teams:

  1. Inventory client-side dependencies. Maintain visibility into what third-party scripts run on your sites.

  2. Assess third-party vendors. Evaluate the security practices of companies whose scripts you load.

  3. Implement client-side protection. Consider specialized tools for monitoring runtime JavaScript behavior.

  4. Define policies for script inclusion. Require security review before adding new third-party scripts.

  5. Test CSP implementation. Regularly verify that Content Security Policy is implemented correctly.

  6. Plan for third-party compromise. Know how you'll respond when a third-party script is compromised.

For Organizations Using Cryptocurrency or Payment Applications:

  1. Minimize runtime dependencies. Bundle as much as possible at build time with verified versions.

  2. Implement strict CSP. Payment flows warrant the development investment for tight CSP policies.

  3. Use SRI universally. Every external script in payment contexts should have integrity verification.

  4. Monitor transaction anomalies. Detection systems should flag unusual transaction patterns that might indicate script compromise.

  5. Consider isolation. Load sensitive functionality (wallet connections, payment forms) in isolated contexts.

Client-side JavaScript supply chains represent perhaps the most immediate supply chain risk: compromises affect users within milliseconds, without any action by site operators. The Ledger Connect Kit attack demonstrated that sophisticated attackers understand this leverage. While SRI and CSP provide partial protection, the fundamental challenge remains: every external script is a continuously trusted dependency, executing with full access to your users' browsers.


  1. HTTP Archive, "Web Almanac 2024: JavaScript," https://almanac.httparchive.org/en/2024/javascript