A network round trip and a long-lived JavaScript object were enough to restore two things Cloudflare had deliberately denied potential Spectre attackers: a useful clock and time to work. In a production test against Workers controlled by its own researchers, Cloudflare leaked an intentionally planted JWT from a neighboring tenant at up to 12 bits per second with 99.16% accuracy. The result matters to anyone building a multi-tenant runtime because it shows how ordinary platform features can reconnect an attack chain after the obvious low-level primitives have been removed.
Cloudflare says the demonstrated path is already blocked in production and that it found no sign of active exploitation during the past three years. The company published its account of the test on August 19, alongside a research paper covering work performed in 2024 and early 2025. This was an authorized experiment: the attacker Worker, victim Worker, remote timer, and secret all belonged to the research team.
Why Workers made this a hard security problem
Cloudflare Workers runs untrusted JavaScript close to users around the world. Tens of thousands of tenants can share an operating-system process, while separate V8 isolates give each Worker its own JavaScript heap. That arrangement avoids much of the startup and memory cost of assigning every tenant a process, but it also means an arbitrary read inside one shared process could expose another tenant's data. Cloudflare surrounds V8 with Linux namespaces, seccomp filters, Cap'n Proto RPC, automated V8 patching, and the option to move suspicious scripts into separate processes.
Spectre attacks do not require the processor to commit an illegal read as a normal instruction. They manipulate speculative execution, the CPU's attempt to run likely instructions before it knows whether a branch is correct. When the prediction is wrong, the architectural result is discarded, but traces can remain in cache state. An attacker can encode a secret bit into that state and infer it by measuring whether a later memory access is fast or slow.
Workers already removed the usual measuring tools. During CPU-only execution, Date.now() and performance.now() do not advance as continuous high-resolution clocks. The runtime also disallows shared memory and multithreading, closing off the familiar counter thread built with SharedArrayBuffer. Cloudflare's 2021 response added Dynamic Process Isolation, or DyPrIs, which watches hardware performance counters and moves scripts that resemble Spectre attacks into their own processes.
The new work found that each defense could be sound in isolation while the full platform still supplied substitutes. The paper's abstract reports an improvement from the earlier attack's two bits per minute to as much as 12 bits per second. That is a 360-fold increase in the top reported leakage rate, though the result applies to the team's controlled setup and chosen production conditions rather than every Worker invocation.
A remote clock was noisy, but good enough
A cache hit differs from a miss by nanoseconds. A timer reached over a network varies by microseconds or milliseconds, apparently far too imprecise for the job. The researchers overcame that mismatch with an amplification technique based on the CPU cache's tree-based pseudo-least-recently-used replacement policy. A carefully chosen access pattern turns one cache event into many hits or misses, widening the timing difference until a remote observer can classify it.
The clock itself came through a WebSocket connection to an external server that returned high-resolution timestamps. The Worker marked the start and end of an amplified measurement, then asked the server for the elapsed time. Cloudflare reports that several timer arrangements produced sub-millisecond median resolution with only a handful of samples, even when the timer was farther away in the network. A hostile script did not need access to a precise local clock once it could outsource timing.
Repeated measurements also needed data that was probably outside the cache at the start of each round. Rather than laboriously constructing an exact eviction set through a noisy timer, the attack allocated a pool much larger than the cache and selected fresh random object pairs. Cloudflare gives a simple example: with 64 MB allocated against a 256 KB L2 cache, a randomly selected line has at most a 1-in-256 chance of still residing there. The method traded precision about one address for a high probability that the next address was already cold.
The Spectre gadget then used speculative type confusion. After training a branch with objects of the expected type, the code supplied an object with an attacker-controlled layout. During the mistaken speculative path, a raw 64-bit pointer associated with a typed array allowed an out-of-bounds read. One chosen bit selected a probe-array cache line, converting the secret into the timing signal. The researchers first recovered an isolate heap root, then confirmed arbitrary-address reads against the Linux vDSO, where strings such as gettimeofday gave them recognizable output.
Durable Objects turned a request into hours of execution
An attacker and victim also had to land in the same process on the same edge server. According to Cloudflare's test, a subrequest from the attacker to the victim would usually cause the scheduler to start the victim Worker in that process. Repeated requests kept it alive. The team could favor an off-peak data center, where lower CPU load reduced noise, and used Cloudflare's /cdn-cgi/trace response to help confirm placement.
Execution limits should have constrained the experiment. At the time, the relevant caps were 30 seconds of CPU time and 1,000 subrequests per invocation. Durable Objects changed the useful unit of work. They are designed for persistent coordination, and each incoming WebSocket message reset the applicable time and request allowances. Keep-alive messages let the researchers preserve one isolate and a bidirectional channel for between five and more than 20 hours, provided synchronous bursts yielded to the event loop before the runtime killed them.
That persistence exposed the first DyPrIs blind spot. The detector isolated a suspicious script after an invocation finished, while this invocation could remain active long enough to complete the leak. A second weakness came from the detector's normalization formula. DyPrIs compared branch mispredictions with instruction translation lookaside buffer accesses. The I/O loop used by the remote timer raised the latter count, pushing the ratio below the threshold and making the attack resemble an ordinary I/O-heavy Worker.
After establishing the timer, co-location, and persistence, the researchers calibrated the noisy measurements against values they could reach speculatively. They then leaked a JWT placed inside the victim Worker, one bit at a time, using majority voting and a percentile-based threshold. Cloudflare measured more than 99% accuracy at up to 12 bits per second and says higher rates were possible if the attacker accepted more errors. Heavy machine utilization slowed the process but did not make it infeasible in the team's tests.
How Cloudflare closed the demonstrated path
Cloudflare changed DyPrIs to account for long-running invocations and I/O-heavy behavior during execution. Waiting for a Durable Object or WebSocket session to end was too late. The company is also investigating whether repeated timer-like network traffic around compute-heavy sections can become another detection signal. That is an open line of work, not a deployed claim in the report.
The company also integrated V8's memory sandbox. Its aim is to remove raw 64-bit pointers from much of the JavaScript heap, which makes the typed-array technique used by this attack harder to reuse. Cloudflare is explicit that the V8 sandbox does not eliminate every Spectre gadget or variant. It blocks the demonstrated pointer path rather than proving speculative execution harmless.
A hardware boundary arrived in September 2025. Cloudflare's separate account of its in-process isolation explains how Memory Protection Keys, or MPK, assign tenant heaps to protection domains within a process and let the runtime switch access rights cheaply. A memory request carrying the wrong protection key is denied by hardware, blocking the straightforward cross-isolate heap read used in the experiment. MPK has a finite number of domains and requires careful key-state management, so Cloudflare presents it as another reduction in exposure rather than a complete Spectre cure.
For developers using Workers, the report does not call for application changes, and Cloudflare says the production path is mitigated. For operators of shared runtimes, the platform interactions are the useful part of the report. Removing a timer did not remove timing when a Worker could ask another machine. Invocation limits did not bound total attack time when a stateful service renewed the budget. A detector looking at one ratio missed hostile computation wrapped in legitimate I/O. Security reviews of shared runtimes have to model how features compose, including features created for networking and state rather than performance measurement.
The next evidence to watch is whether Cloudflare publishes more detail on remote-timing detection and how the revised DyPrIs behaves against benign, long-running WebSocket workloads. Independent attempts to reproduce the paper's techniques on other isolate-based platforms would also show how much of the result depends on Workers scheduling and Durable Objects. For now, the useful finding is narrower: Cloudflare built a credible cross-tenant leak in its own production environment, then described the exact platform interactions that made its earlier defense incomplete.