---
title: "Supply Chain Roulette"
date: 2026-02-14
description: "I watched a supply chain attack unfold in real-time. Here's what I learned about trusting third-party code."
tags: ["security","supply-chain","zero-trust","incident-response"]
readingTime: "10 min read"
url: https://alexmoening.com/dev-thoughts/supply-chain-roulette.html
markdownUrl: https://alexmoening.com/dev-thoughts/supply-chain-roulette.md
---

# Supply Chain Roulette

[← Back to /dev/thoughts](/dev-thoughts/)

<p class="lead">Every <code>&lt;script src="https://cdn..."&gt;</code> tag is a trust relationship. When that CDN gets compromised, so do you.</p>

### The Convenience Trap

<p class="section-summary">CDN embeds trade security for convenience. Most developers don't think twice about it.</p>

It's the path of least resistance. Need p5.js? Grab it from cdnjs. Need a utility library? unpkg has you covered. The pattern is everywhere:

<pre class="terminal"><code><span class="ansi-gray">&lt;!-- The path of least resistance --&gt;</span>
<span class="ansi-white">&lt;</span><span class="ansi-red">script</span> <span class="ansi-yellow">src</span><span class="ansi-white">=</span><span class="ansi-cyan">"https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.7.0/p5.min.js"</span><span class="ansi-white">&gt;&lt;/</span><span class="ansi-red">script</span><span class="ansi-white">&gt;</span>
<span class="ansi-white">&lt;</span><span class="ansi-red">script</span> <span class="ansi-yellow">src</span><span class="ansi-white">=</span><span class="ansi-cyan">"https://unpkg.com/some-lib@latest/dist/bundle.js"</span><span class="ansi-white">&gt;&lt;/</span><span class="ansi-red">script</span><span class="ansi-white">&gt;</span></code></pre>

Convenient. Fast to implement. And potentially catastrophic.

### When CDNs Go Wrong

<p class="section-summary">One compromised CDN can inject malicious code into millions of sites simultaneously.</p>

In February 2024, polyfill.io was acquired by a Chinese company called Funnull. Within months, the service that millions of websites trusted to provide browser compatibility code began injecting malicious JavaScript. The attack was sophisticated: malicious code only activated for specific user agents, making detection difficult. By the time security researchers caught it, **over 100,000 websites** were compromised.

This wasn't an isolated incident. Magecart attacks, which target e-commerce sites through compromised third-party scripts, have affected over 2 million websites since 2015.

The attack vector is simple: compromise a CDN or package registry, and you instantly have code execution on every site that embeds from that source. No need to hack each site individually. One compromised dependency, millions of victims.

### The Night the Stream Went Dark

<p class="section-summary">I watched a supply chain attack unfold in real-time at Akamai. That hour on the Bravo Bridge changed how I think about trust.</p>

I saw this play out firsthand during my time at Akamai, when I worked as a Solutions Engineer supporting video streaming customers.

One of our customers was Ooyala, a video platform that powered streaming for major media companies. Their architecture relied on a JavaScript player that customers embedded on their sites--and that player pulled in additional assets from Ooyala's CDN at runtime. Configuration files, UI components, analytics scripts. Standard architecture for the era.

Then came the spear phishing email.

Someone inside Ooyala--an engineer or administrator with elevated access--fell for a targeted attack. It's worth saying: this can happen to anyone. Spear phishing is sophisticated, personal, and designed to bypass your natural skepticism. No fault on the user. The fault was in the system that allowed a single compromised credential to cause this much damage.

The attackers got credentials. Not to the code repository, but to something arguably worse: the CDN configuration. They didn't need to change the player code. They just needed to change what the player *fetched*.

The target was a major news broadcaster--a right-leaning network known for its hosts' strong political views. When viewers tuned into their live stream that evening, instead of news, they saw a political message. Hacktivism, delivered through a trusted embed.

Here's the thing: we dodged a bullet. The attack was surgically targeted--it only activated for requests with a referer from that specific broadcaster's domain. Every other Ooyala customer was unaffected. If the attacker had been truly malicious, they could have compromised every site using Ooyala's player. Credential harvesting. Cryptojacking. Keyloggers. Instead, they wanted to make a political statement to a specific audience. The limited scope is what convinced us this was hacktivism, not a criminal operation.

#### The Bravo Bridge

The phone rang. It might have been the first time I got paged into Akamai's "Bravo Bridge"--the company's major incident response channel.

Akamai was known for its incident management practices, and that night I saw why. The bridge followed a formal structure--roles you'll find in any mature [incident response framework](https://www.atlassian.com/incident-management/incident-response/roles-responsibilities):

<table class="data-table">
    <thead>
        <tr>
            <th>Role</th>
            <th>Responsibility</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><code>Incident Commander</code></td>
            <td>Central authority during crisis—coordinates the response, makes decisions, keeps things moving</td>
        </tr>
        <tr>
            <td><code>Communications Lead</code></td>
            <td>Manages stakeholder updates, shields the technical team from external noise</td>
        </tr>
        <tr>
            <td><code>Operations Lead</code></td>
            <td>Directs technical responders, coordinates diagnostic and remediation efforts</td>
        </tr>
        <tr>
            <td><code>Scribe</code></td>
            <td>Documents everything in real-time—maintains timeline, captures decisions for the postmortem</td>
        </tr>
        <tr>
            <td><code>Technical Responders</code></td>
            <td>Subject matter experts from each affected system, hands on keyboards</td>
        </tr>
    </tbody>
</table>

Technical incident management is a discipline unto itself. That night, I gained a deep appreciation for how structure and clear roles can cut through chaos.

But even with that structure, there was nothing we could do but wait.

#### The Longest Hour

Rollback was initiated within minutes of identifying the attack. But CDN configuration propagation was slow back then--changes had to reach edge servers worldwide. Even expedited, rollback took about an hour.

That hour dragged on forever.

Today, CDN configs propagate in seconds. Back then, we watched the clock while a political message played on a major news network's live stream. The broadcaster didn't know what hit them. From their perspective, nothing had changed on their end. Their site was exactly as they'd left it. The attack came through the supply chain--through the embedded player they trusted, fetching assets they never audited.

#### The Aftermath

The relationship with the broadcaster was damaged. Trust, once broken, is hard to rebuild.

But something unexpected happened with Ooyala. The incident actually strengthened the bond between Akamai's account team and Ooyala's technical team. They were impressed by our response during a difficult incident and wanted to help us understand what went wrong from their perspective. That collaboration led to hard conversations about what needed to change.

The postmortem was sobering:

<table class="data-table">
    <thead>
        <tr>
            <th>Lesson</th>
            <th>Action</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><code>MFA everywhere</code></td>
            <td>Critical infrastructure requires multi-factor authentication. One password shouldn't unlock production.</td>
        </tr>
        <tr>
            <td><code>Lock the front door</code></td>
            <td>CDN configs are your entry point—treat them like the keys to the kingdom. Double-lock them.</td>
        </tr>
        <tr>
            <td><code>Zero trust</code></td>
            <td>Even authorized users must prove who they are for sensitive operations. Assume credentials can be compromised.</td>
        </tr>
        <tr>
            <td><code>Least privilege</code></td>
            <td>The engineer didn't need write access to production CDN configs. Neither does anyone else by default.</td>
        </tr>
        <tr>
            <td><code>Revoke and rotate</code></td>
            <td>Compromised credentials trigger immediate key rotation across all systems.</td>
        </tr>
        <tr>
            <td><code>IP allowlisting</code></td>
            <td>Lock down the CDN control channel to authorized IPs only. More brittle, but security is about trade-offs.</td>
        </tr>
    </tbody>
</table>

That last one became a product feature. After the incident, we instrumented IP allowlisting for CDN configuration APIs. Customers could now restrict who could push changes to their delivery configs--only requests from their authorized IPs would be accepted. Yes, it made things more brittle. Any config change had to come from a specific IP owned by the customer. But that brittleness was the point. It raised the bar for attackers significantly.

This happened early in my Akamai career, before "zero trust" was a buzzword. But the lesson was clear: trust no single authentication factor, trust no single access control, and definitely don't trust that someone "authorized" is actually who they say they are. CDN configurations are the front door to your entire delivery architecture. You wouldn't protect your front door with just one lock.

### Securing the Pipeline

<p class="section-summary">The real threat isn't just external attackers—it's anyone who remembers a pipeline password after they leave.</p>

To be clear: none of these modern protections existed when the Ooyala incident happened. Multi-factor authentication for infrastructure was rare. Zero trust was an academic concept. We did the best we could with what we had.

Today, you have options. The average insider threat incident costs **$17 million** (2025 data). The most dangerous scenario isn't a sophisticated external attacker--it's an admin who leaves your company and still remembers how to push to your CDN configuration pipeline.

Modern infrastructure provides multiple signals to verify that a request is legitimate:

<table class="data-table">
    <thead>
        <tr>
            <th>Method</th>
            <th>How It Works</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><code>Mutual TLS (mTLS)</code></td>
            <td>Both client and server authenticate with certificates</td>
        </tr>
        <tr>
            <td><code>OIDC federation</code></td>
            <td>CI/CD exchanges a token for temporary credentials—no long-lived keys</td>
        </tr>
        <tr>
            <td><code>IP allowlisting</code></td>
            <td>Restrict access to known source IPs</td>
        </tr>
        <tr>
            <td><code>Adaptive MFA</code></td>
            <td>Adjust auth requirements based on real-time risk signals</td>
        </tr>
    </tbody>
</table>

The key insight: **layer these methods**. A request to update your CDN configuration should require multiple signals--not just a valid API key, but also: originating from an allowed IP, using a short-lived token from OIDC federation, with mTLS verifying the runner's identity.

### The Self-Hosting Alternative

<p class="section-summary">Verify once at build time, serve a known-good copy forever.</p>

For the code you embed on your sites, the solution is straightforward: host your dependencies yourself. Download the libraries you need, audit them, and serve them from your own infrastructure.

<pre class="terminal"><code><span class="ansi-gray">&lt;!-- Before: Trust the CDN --&gt;</span>
<span class="ansi-white">&lt;</span><span class="ansi-red">script</span> <span class="ansi-yellow">src</span><span class="ansi-white">=</span><span class="ansi-cyan">"https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.7.0/p5.min.js"</span><span class="ansi-white">&gt;&lt;/</span><span class="ansi-red">script</span><span class="ansi-white">&gt;</span>

<span class="ansi-gray">&lt;!-- After: Trust yourself --&gt;</span>
<span class="ansi-white">&lt;</span><span class="ansi-red">script</span> <span class="ansi-yellow">src</span><span class="ansi-white">=</span><span class="ansi-cyan">"/lib/p5.min.js"</span><span class="ansi-white">&gt;&lt;/</span><span class="ansi-red">script</span><span class="ansi-white">&gt;</span></code></pre>

This shifts the trust model. Instead of trusting that every request to a third-party CDN returns the same safe code, you verify once at build time and serve a known-good copy.

But self-hosting is only half the solution. You still need to verify what you're hosting. That's where a security pipeline comes in--hash verification, pattern scanning, and human review before anything reaches production.

I've written a companion piece on [implementing a security audit pipeline](auditing-your-dependencies.html), including how to use LLMs for code review (and why you shouldn't trust them too much).

### The Lesson

That incident shaped how I think about third-party embeds to this day. When you embed a script, you're not just trusting the code you see--you're trusting everyone who can change what that script loads, every configuration it reads, every asset it fetches. The attack surface extends far beyond the initial `<script>` tag.

The polyfill.io incident proved that even well-established CDNs can become attack vectors overnight. Taking control of your embeds--and securing the pipelines that deploy them--is the only way to ensure your supply chain stays secure.

<blockquote class="pull-quote">Every external script is an implicit trust relationship. Make it explicit.</blockquote>

### Resources

<p class="section-summary">Further reading on supply chain security and incident response.</p>

<table class="data-table">
    <thead>
        <tr>
            <th>Resource</th>
            <th>Topic</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><a href="https://sansec.io/research/polyfill-supply-chain-attack" target="_blank" rel="noopener">Sansec</a></td>
            <td>Polyfill.io Supply Chain Attack</td>
        </tr>
        <tr>
            <td><a href="https://blog.cloudflare.com/navigating-the-maze-of-magecart/" target="_blank" rel="noopener">Cloudflare</a></td>
            <td>Navigating the Maze of Magecart</td>
        </tr>
        <tr>
            <td><a href="https://www.cisa.gov/resources-tools/resources/insider-threat-mitigation-guide" target="_blank" rel="noopener">CISA</a></td>
            <td>Insider Threat Mitigation Guide</td>
        </tr>
        <tr>
            <td><a href="https://cheatsheetseries.owasp.org/cheatsheets/CI_CD_Security_Cheat_Sheet.html" target="_blank" rel="noopener">OWASP</a></td>
            <td>CI/CD Security Cheat Sheet</td>
        </tr>
        <tr>
            <td><a href="auditing-your-dependencies.html">Part 2</a></td>
            <td>Auditing Your Dependencies (implementation guide)</td>
        </tr>
    </tbody>
</table>

---

## Navigation

- [Home](/)
- [About](/about.html)
- [Projects](/projects.html)
- [Contact](/contact.html)
- [/dev/thoughts](/dev-thoughts/)

*Copyright 2026 Alex Moening. Opinions expressed are my own.*
