12 bits per second from a neighboring worker: how a remote timer bypassed Cloudflare Workers protection

On August 17, arXiv posted the preprint «Remote-Timer-as-a-Service: Efficient Microarchitectural Leakage in the Cloud with Remote Timers». Martin Schwarzl, Haochen Xiao, Albert Pedersen, Sam Einsworth and Nigel Tophem pulled JWTs from memory on another worker at Cloudflare Workers — at a speed of 12 bits per second with 99.16% accuracy. Cloudflare says the protection was further improved in 2025. But what’s more interesting than the leakage itself is the question: how do you even measure nanoseconds on a platform that doesn’t have a clock in runtime by default.

One process per thousand tenants

Workers do not give each client a separate process. Cloudflare’s documentation states this plainly: “if Cloudflare had to run a separate process for every guest, the number of tenants Cloudflare could support would be drastically reduced.” Instead of processes — isolated V8 instances: the boundary between tenants is not the OS kernel, but the JavaScript engine itself.

Info:

Isolating V8 — an independent instance of the engine with its own heap and its own global object. In a single process there can be thousands of them. For JavaScript code they are completely isolated. For the processor — it’s the same physical memory and shared caches.

Spectre breaks exactly this model: speculative execution knows nothing about isolates, it knows addresses. Cloudflare additionally slices machines into “cordons” — distributing tenants of different plans across different runtime instances, but neighbors inside a cordon remain.

A timer that doesn’t exist

Any cache attack relies on measuring time, and this is where Cloudflare tightened the screws: Date.now() does not move during code execution — the value only updates after I/O. No threads, no SharedArrayBuffer, a self-made counter from a two-thread race cannot be assembled.

The authors’ answer — move the clocks outside the platform: the attacker’s server measures time from network responses, and the one-bit difference is amplified by microarchitectural effects to a magnitude noticeable even over the network. According to The Hacker News analysis, the role of the remote timer is played by the WebSocket exchange, and isolates stay alive between requests through Durable Objects; the abstract details are not disclosed.

The detector that learned not to wake up

A funny detail: the defense that was broken was devised by the same group. In 2021 Schwarzl and co-authors demonstrated a leak of 120 bits per hour and proposed Dynamic Process Isolation — migrating to a separate process only those workers whose performance counters look suspicious (reported false positive rate: 0.61%). The new work states: “the production implementation of DyPrIs was insufficient.”

The speed gap should be translated into familiar units. 12 bits/s is 43,200 bits per hour, 360 times more than the previous result. A 300-byte token (2400 bits) at that rate would leak in about three and a half minutes instead of twenty hours — this is no longer a lab curiosity, but a working window. My calculation; the paper itself does not include such an estimate.

What was closed and why this isn’t the end

From the preprint abstract, after coordinated disclosure Cloudflare rolled out three things: V8 Sandbox limitations, enhanced detection in DyPrIs, and hardware isolation of the tenant heap via memory protection keys. Part of this is described in the company’s blog “Safe in the sandbox” from September 25, 2025. The claim that there were no exploitation indicators over three years is the vendor’s position, and no one has verified it.

Warning:

The cycle closed a second time: attack → defense → defense circumvention. Cloudflare itself states in its docs: “Spectre does not have an official solution. Not even when using heavyweight virtual machines.” This question cannot be considered closed on any multi-tenant platform.

The practical takeaway here is not about Cloudflare. Language isolation is cheaper than process isolation, and it is chosen wherever density matters: shared hosting, multi-tenant serverless runtimes, your own service running user JavaScript in a single Node process. If your threat model includes a neighbor on the same hardware, the boundary lies somewhere other than where the code suggests.

Success:

What you can do right now, without waiting for the next paper:

  • check the actual lifetime of your tokens — the shorter it is, the smaller the window for slow leakage;
  • don’t keep long-lived keys in edge-function memory: request on demand and don’t cache in a global isolate area;
  • for truly secret computations, choose an environment with process-level or hardware boundaries, not language-based ones.
JWT='eyJhbGciOi...'   # your token
python3 - <<'EOF'
import base64, json, os
t = os.environ["JWT"].split(".")[1]
p = json.loads(base64.urlsafe_b64decode(t + "=" * (-len(t) % 4)))
print("iat", p.get("iat"), "exp", p.get("exp"), "→ lifetime", p["exp"] - p["iat"], "s")
EOF

Export JWT before running (export JWT='...'). If the difference between exp and iat is measured in days, any slow leakage has an infinite window; if in minutes, the stolen token will expire before it’s even read.

Related threads on the forum: auto-insertion of Cloudflare scripts on a static site and five days with a live token in GitHub Actions.

Sources

Question:

Do you even consider hardware neighbors part of your threat model? Or do you still assume the cloud will handle it somehow? I’m curious if there are people here who, because of such stories, consciously moved from serverless platforms to their own VPS.