Isolate sharing is a Spectre channel on Workers

CloudflareSecurityServerlessEdge ComputingArchitectureDeveloper Tools

August 21, 2026

Light chalk on dark asphalt, two room squares sharing a cracked vent, with the words a Spectre channel scratched beside them

Workers Spectre stopped being a 2021 footnote on 19 August 2026. Cloudflare's August 2026 writeup says their own researchers leaked a JWT they had placed in a co-located Worker, in production, at up to 12 bit/s with more than 99 percent accuracy.

The object lived in isolate heap. Cloudflare says the presented attack is already mitigated in production. Isolate sharing is still how Workers stay cheap, and still the channel that gadget used.

If you already treated V8 isolates as the wall, the documented model failed a production test. The V8 sandbox plus Memory Protection Keys closed the gadget they used. Cloudflare says neither layer is a complete Spectre answer, so heap-versus-env is defense in depth, not a new guarantee.

This is not a walkthrough of the arXiv paper. The useful question is what may live in isolate heap versus env now.

Isolates share a process on purpose#

A chalkboard drawing of one process box holding three heap rooms with a faint dotted vent between them
Every tenant gets a room, and the heating vents still connect them.

Cloudflare Workers run untrusted JavaScript on the edge by packing tens of thousands of tenants into one operating-system process. Each Worker gets its own JavaScript heap. That is still the lead sentence in the writeup.

The density is the product. Process isolation would turn every idle Worker into a luxury SKU. Cloudflare's security-model docs say it straight.

Thousands of active tenants per machine. Guests that cannot take more than a couple megabytes. Context switches in the thousands per second.

Timers freeze while the isolate is crunching. Date.now() sits still. No shared memory, no extra threads, so you cannot even build a counter-thread clock.

Dynamic Process Isolation watches performance counters. If a script looks like Spectre, it parks that script in its own process after the invocation.

Think of the process as a boarding house. Every tenant gets a room. The doors lock. The heating vents still connect the rooms.

That model is generous and mostly true. It is also why a neighbor heap is a cache-shaped object, not a different computer.

A fetch sits you next to the victim#

Chalk drawing of an attacker heap sending a fetch arrow so a victim heap appears beside it in the same process
Co-location is a fetch, and the scheduler is doing its job.

People imagine co-location as a cloud scavenger hunt. On Workers it is a fetch.

The writeup is blunt. Invoking the victim script from the attacker script with a fetch to the victim URL will in most cases cause the scheduler to spin up an instance of the victim Worker in the exact same process. Keep it warm with subrequests.

The scheduler is doing its job. You can even pick a quiet colo so the noise drops. Australian afternoon, European morning.

That is not a 0-day. That is load.

If you ship a public Worker URL, a neighbor can ask the platform to sit you in the same process. The isolation story never claimed otherwise. It claimed the heap wall would hold once you got there.

Keep-alive is how DyPrIs missed#

A long chalk keep-alive line holding one isolate open, with DyPrIs sitting too late at the far right
The leak finished while DyPrIs waited for the invocation to end.

DyPrIs was the grown-up defense from 2021. Watch the counters. Isolate the weirdo after the request ends.

The 2021 remote attack leaked about 120 bit/h. Slow enough that a 30-second CPU budget and a post-invocation move felt like enough.

Durable ObjectsDurable ObjectA Cloudflare Workers primitive that gives a single instance of code its own persistent storage and guaranteed single-threaded execution, used to hold state a stateless server can't.See also McpAgent, state handle treat every WebSocket message as a fresh invocation that resets CPU time. Hold the socket. Ping.

That isolate stays up for five hours, then more than twenty. The leak finishes while DyPrIs is still waiting for the invocation to end.

A remote timer is one large network loop. That inflates instruction-TLB activity. DyPrIs divides branch mispredicts by those instruction-TLB accesses.

On the page, the ratio looks like a chat Worker, not a Spectre Worker.

A coordination primitive built for chat just held the door. You will hit this if you assumed suspicious scripts get their own process without asking when the process split actually happens.

The leak was a heap leak#

Split chalkboard. Left heap box leaks a JWT along a dotted path. Right env box keeps a secret with no leak path.
The JWT they leaked was in the heap, a secret you never copy out of env is a different surface.

They did not steal a customer token. They put a JWT in a Worker they owned, then read it bitwise from a neighbor isolate.

First byte was e. Rate up to 12 bit/s. Accuracy more than 99 percent.

Same channel as 2021, about 360 times faster. The leak is a heap leak. Speculative type confusion through a TypedArray backing pointer, then a walk from the isolate root to a JavaScript value.

Bindings that never copy the secret into JavaScript are a different surface. Workers secrets docs still show the copy.

import { env } from "cloudflare:workers";
import postgres from "postgres";

// Initialize the database client at the top level using a secret
const sql = postgres(env.DB_CONNECTION_STRING);

That connection string is now a heap object. It lives for as long as the isolate does.

This site is a Worker. TURNSTILE_SECRET_KEY and VISITOR_COOKIE_SECRET stay on env and get read per request. Rotate the visitor cookie secret and every guest falls out of their own likes.

That secret is exactly the JWT-shaped thing the paper leaked if it gets copied into module scope.

R2 and KV stubs do it the other way. The permission is the API. The underlying secret never shows up in your code.

Third-party tokens do not get that courtesy unless you refuse to copy them. Heap is whatever you assigned to a const at the top of the file. A secret you never copy into a long-lived JavaScript value is off the demonstrated heap-read surface.

That is defense in depth, not a proof that env is invisible to every Spectre gadget. Mixing the two is how a secret binding becomes the heap object this gadget walked.

  • Heap holds whatever you assigned to a const at module scope, including a JWT copied out of env.
  • A secret that stays on the binding and never becomes a long-lived JavaScript value is off the object graph this gadget walked.
  • R2 and KV stubs already work this way. Third-party tokens only do if you refuse the copy.

Child isolates sit on the same channel#

A parent that loads model-written source is still sharing the process. The Dynamic Workers post already covered capability injection and the open globalOutbound default. This is the underside of that same isolate.

Hand the child API_TOKEN as a string and you put that token in guest heap. Keep the bearer on the loader and stamp it in a gateway, and the child never sees it. Dynamic Workers egress control already describes that pattern.

Treat it as Spectre hygiene, not a style preference.

A Kitesurf page is the same guest with a DOM. Session tokens in page JS are heap.

Long auth is already on the cannot-do list, which is the right Spectre move even if they made it for product reasons. Keep it there.

Cloudflare leaked a classic Worker plus a Durable Object, not a Kitesurf tab. Same heating vent.

Keep secrets off the isolate heap#

The September 2025 hardening post put isolate heaps behind Memory Protection Keys. About 12 keys on a modern x64 CPU. Random assignment traps roughly 92 percent of cross-isolate reads.

Packed sandboxes and rotating keys close the rest for the in-sandbox model they described. The V8 sandbox strips raw 64-bit pointers from most of the heap. The gadget they used does not work anymore.

Cloudflare still says MPK is not a complete Spectre answer. The V8 sandbox is not a complete Spectre mitigation. Isolate sharing is still how the platform runs.

Heap is for public copy and warm caches. The public Turnstile site key lives in this bundle on purpose. The glossary dictionary sits in module scope because it is decoration, not a token.

Keeping secrets on env and reading them inside the handler, never copying them into a client that outlives the request, shrinks the demonstrated heap-read surface. It is not a guarantee that env sits outside every possible Spectre-reachable representation.

  • Public copy, site keys, and warm decoration caches can live in isolate heap.
  • Turnstile secrets, cookie-signing keys, and third-party tokens stay on env and get read inside the handler, as defense in depth.
  • Child Dynamic Workers get capabilities, not string tokens, unless you want those tokens in a guest heap.

You ship a module-scope client and you put a JWT-shaped object back on the channel. The patch narrowed the vent. It did not brick up the boarding house.

Questions the leak actually raises

Why don't Workers just give every tenant a process?

Process isolation would make edge compute a luxury product. Cloudflare's security-model docs say thousands of active tenants per machine, with guests that cannot take more than a couple megabytes, and context switches in the thousands per second. Isolate sharing is how that density exists. It is also how a neighbor heap became a Spectre target.

asked on developers.cloudflare.com
Was any customer JWT actually stolen?

No. The paper says the attack only aimed at worker scripts the researchers controlled, and no customer data has been accessed. They placed the JWT in a victim Worker they owned, then read it bitwise in production. The channel is real. The victims were not your customers.

asked on arxiv.org
Should multi-tenant edge be a bigger concern after this?

The concern is isolate heap, not the brand. A secret that stays on env and never becomes a JavaScript value is a different surface than a module-scope client initialized from env. Cloudflare's own secrets docs still show that copy. Treat the copy as the object a neighbor can leak, even after MPK narrowed the gadget.

asked on reddit.com
Is the demonstrated attack still live?

Cloudflare says the presented attack is already mitigated in production by the V8 sandbox, Memory Protection Keys, and improved DyPrIs, and they found no indicators of active exploitation over the last three years. They also say MPK is not a complete Spectre answer. Isolate sharing is still the architecture.

asked on blog.cloudflare.com
Share

Newsletter

New posts land in your inbox when they publish. No spam, unsubscribe anytime.

Prefer RSS