Skip to main content

On 19 August 2026, Cloudflare published research in which its own security team leaked a JSON Web Token out of one Worker and into another, across the tenant boundary, on the production edge, at up to 12 bits per second with better than 99% accuracy. The vulnerability is closed: mitigations shipped before publication, and Cloudflare reports no indicators of exploitation over the three years the technique was viable.

This is not a “get off Cloudflare” post. Workers remains an excellent platform and the disclosure is a model of how this should be handled. It is a post about what the research demonstrates, which is more useful than the bug: the isolation boundary you trust in a shared-tenancy runtime is a property of hardware, scheduler behaviour and detection heuristics, not a line in an architecture diagram.

TL;DR

  • Cloudflare’s security team reproduced a remote Spectre attack against Workers in production, leaking a secret from a co-located victim isolate at up to 12 bits per second with over 99% accuracy, roughly a hundredfold improvement on its 2021 assessment.
  • Workers uses soft multi-tenancy: tens of thousands of tenants share one operating system process, separated by V8 isolates rather than process or VM boundaries. That is why cold starts are near zero and the economics work.
  • Co-locating with a target was trivial. Calling fetch() against the victim’s hostname usually causes the scheduler to start it in the same process, and an attacker can pick an off-peak data centre for quieter conditions.
  • Detection missed it because the attack looked like an ordinary I/O-heavy Worker, and the check only ran after an invocation finished.
  • The fixes (V8 sandbox, memory protection keys for in-process isolation, reworked detection) shrink the leakage surface without eliminating the class. Decide deliberately which of your workloads may share a process with strangers.

What soft multi-tenancy buys, and what it costs

Workers runs untrusted JavaScript from tens of thousands of customers inside shared operating system processes, using V8 isolates for separation. Each Worker gets its own JavaScript heap; the process is shared. That is the entire reason the platform feels the way it does: isolates start in single-digit milliseconds and cost almost nothing to keep warm, which is what makes per-request pricing and a global footprint viable. Process or VM isolation per tenant would price the product out of existence.

The cost is that a single arbitrary read primitive inside that process reaches other tenants’ memory. Cloudflare has known this since 2021, which is why the platform freezes timers during CPU-only execution, removes SharedArrayBuffer and multithreading, shuffles memory, and runs a system called Dynamic Process Isolation that watches hardware performance counters and moves suspicious scripts into their own processes.

Speculative execution is what makes those defences insufficient on their own. The CPU runs ahead of a branch it has guessed at, and when it guesses wrong it discards the results but leaves traces in the cache. An attacker who can steer a speculative out-of-bounds read, encode one bit of it into cache state and then time a read of a known address has a channel out of the sandbox. No memory safety bug is required; the hardware is doing what it was designed to do.

What made an impractical attack practical

The 2021 assessment concluded the attack was too slow to matter at 120 bits per hour. Three developments changed that arithmetic, and none of them required a new bug. Work by Stephen Röttger and Artur Janc showed how the tree-based replacement policy in L1 caches can stretch a single cache event, a few nanoseconds wide, into a run of hits or misses long enough for a noisy network clock to read. Dougall Johnson’s pigeonhole trick replaced the expensive business of building cache eviction sets with a large allocation and a random location each round. And since Workers deliberately provides no high-resolution timer, the researchers used someone else’s: a WebSocket connection to a timestamp server in a nearby data centre. Together those took the rate to 12 bits per second, enough to read a JWT out of a co-located victim Worker one bit at a time.

The two details that should change how you think

The microarchitectural engineering is not the part that generalises. Two operational details are.

The first is co-location. You might assume that landing on the same machine as a specific target, across tens of thousands of edge servers, is the hard part. It was the easy part. Because a Worker can run anywhere, calling fetch("https://victim.example") usually causes the scheduler to start the victim in the same process, and repeated subrequests keep it warm. Since stability depends on machine load, the attacker can also pick an Australian data centre during European business hours. Scheduler convenience features become targeting primitives once the threat model includes your neighbours.

The second is why detection failed, which is the part worth taking to your own architecture review. Dynamic Process Isolation watched the right signals and still missed this. It isolated a script only after its invocation completed, and the researchers used a Durable Object with WebSocket keep-alive messages to hold one invocation open for hours, so the leak finished long before the check ran. Its heuristic also normalised branch mispredictions against instruction TLB accesses, and the constant timer traffic inflated that denominator until the ratio fell below the threshold. The attack looked like a chatty, I/O-heavy Worker, which is a completely ordinary thing to be.

That failure mode shows up well outside Spectre research. A threshold calibrated on one workload shape stops working when the shape changes, and long-lived streaming connections are exactly what breaks “check it when it finishes”. If your monitoring assumes bounded request lifetimes, go and look at what your WebSocket, SSE and agent traffic is doing to it.

What Cloudflare changed, and what it does not fix

Three things. The V8 sandbox removes raw 64-bit pointers from much of the JavaScript heap, which kills the specific type-confusion gadget used here. In September 2025 Cloudflare deployed in-process isolation using Memory Protection Keys, so each isolate’s heap sits behind a hardware-enforced access boundary within the shared process. And Dynamic Process Isolation now treats long-lived and I/O-heavy executions as first-class cases rather than noise.

Cloudflare is admirably clear that none of this is a complete mitigation: the sandbox blocks known gadgets rather than the class, and MPK offers a finite number of hardware domains. The surface got smaller and attacks got more expensive, which is what defence in depth looks like when the underlying issue is in silicon.

The decision this forces on your architecture

Isolation is a spectrum with a price at every point, and most teams pick one by accident: whatever their platform defaults to. Language-level isolation gives near-zero cold starts and the lowest cost with the weakest boundary; containers give kernel-enforced separation with shared kernel risk; microVMs such as Firecracker give a hypervisor boundary for a modest startup penalty; dedicated hardware gives the strongest boundary at the highest cost. The right answer is rarely one point for the whole estate:

  1. Classify workloads by what is resident in memory, not by what they do. A Worker proxying requests with a short-lived token is a different risk from one holding signing keys, decrypted health records or another customer’s data in the same heap.
  2. Move long-lived high-value secrets out of the shared process. Signing and decryption belong behind a service you control, a key management service or an HSM. If the secret never enters the shared heap, a heap disclosure does not reach it.
  3. Shorten what a leak is worth. Twelve bits per second is roughly 90 bytes a minute. Short token lifetimes, aggressive rotation and narrowly scoped credentials turn a slow exfiltration channel into a race the attacker often loses.
  4. Ask your provider the specific question. Not “is it secure” but “what enforces the boundary between my tenant and another, and can I pay for a stronger one?” Most serious platforms offer a dedicated tier; very few customers ask what it is for.
  5. Check whether your detection assumes requests end. Post-execution analysis and baselines built on request/response traffic degrade quietly once persistent connections are normal, and with agent workloads they already are.
  6. Write the boundary into your data protection documentation. If regulated data lives in a shared-process runtime, your DPIA and client security questionnaires should already say so. Discovering it during due diligence is worse.

The uncomfortable general lesson

Every abstraction that makes deployment cheaper relocates a responsibility rather than removing it. Serverless moved concurrency management to you; managed CI moved workflow permissions to you; soft multi-tenancy moves the isolation boundary into a runtime and a CPU you cannot inspect, then leaves you to decide what deserves to sit behind it. The answer is not to abandon edge compute. It is to know, per workload, which boundary protects it and who maintains it. Cloudflare attacked its own platform, found the gap, fixed it and published the lot, including the part where its detection was fooled. That is considerably more than you get from vendors who have never looked.

At REPTILEHAUS we design, build and operate production systems for entrepreneurs, management teams and other agencies, including the parts that only surface in a security review: where secrets actually live at runtime, and which tenancy model each workload sits in. If you cannot say which isolation boundary protects your most sensitive workload, get in touch.


📷 Photo by Winston Chen on Unsplash