deniz.in

Markets

Weather

Loading weather

· via dev.to (home feed)

Researcher documents cross-user SSR cache leak in React data fetching library

A researcher demonstrated that a popular React data fetching library keeps its SSR cache in a module-level Map, letting one user's data be served to another during concurrent server renders. The isolation fix is opt-in.

Researcher documents cross-user SSR cache leak in React data fetching library

A shared cache on the server

A security researcher has documented a cache isolation failure in a popular React data fetching library, showing that its default configuration lets cached data flow between concurrent users during server-side rendering. According to a write-up on dev.to, the researcher was auditing the library — the kind commonly seen in Next.js projects — and traced its cache initialization to a module-level new Map(), created once and referenced by every part of the library.

That design is sensible in the browser, where a single long-lived cache should persist as the user navigates between pages. But a Node.js process loads each module once and shares it across every incoming request. The researcher reports finding no request-scoped isolation in the default path: no AsyncLocalStorage wrapper, no per-request cache factory, no SSR-specific branch that hands out an isolated cache.

How data crosses between users

The failure requires no attacker and no malicious input. The write-up walks through a component that fetches user-specific data during SSR under a single cache key such as /api/user-data. Because that key is identical for every user, the sequence is straightforward: user A's render populates the cache, user B's render a few milliseconds later finds A's payload under the same key and reuses it, and user B's HTML then contains A's name, email, or whatever else the payload held.

To confirm the behavior, the researcher built a proof of concept simulating three concurrent requests from three different users hitting the same cache key. The output, quoted in the post, states that users B and C received user A's private data from the shared cache. No exploit chain and no timing attack — just ordinary traffic arriving at the same time.

The example component in the post calls a useSWR-style hook, but the researcher has withheld the library's name and the vendor's response, citing responsible disclosure practice.

The secure option is opt-in

The library does ship a provider component that creates a fresh cache scope and would keep requests separated, the write-up explains. The problem is that it is opt-in: developers following the default setup get the shared singleton, and the documentation does not foreground the SSR risk. The researcher argues that a protection developers must discover and enable is, in practice, no protection at all, because most integrators never learn the danger exists.

The recommended fix has two parts. First, make request-scoped caching the server default, using something like AsyncLocalStorage. Second, document the SSR risk far more explicitly — which the researcher calls the harder half, since a documentation gap cannot be patched and cannot force anyone to read.

A familiar pattern

The finding is framed as one instance of a broader class: module-level mutable state in server-side JavaScript. Configuration stored in a top-level object, a logger buffering in module scope, a session manager initialized once — the details vary, but all rest on the same mistaken assumption that state created once at module scope is safe to share across requests.

What makes this bug class hard to catch, the write-up argues, is that local development rarely exposes it. A developer testing alone issues one request at a time, so the cache never collides with anything. The leak only appears in production, under load, when two users happen to hit the same cache key simultaneously.

Why it matters

Server-side rendering is now a default architecture for a large share of React applications, and data fetching libraries sit directly in the path of user-specific requests. If their caches are process-global singletons, every SSR app running the default setup carries a plausible cross-user data leak that no attacker needs to trigger and no routine scan will flag. The finding is also a reminder that concurrency safety lives in defaults rather than features: the isolation mechanism already exists in this library, but it only protects developers who already knew to look for it. Until request-scoped caching is the out-of-the-box behavior on the server, teams running SSR with shared cache keys should audit their data layer — or at minimum verify whether they actually wrapped their app in the isolation provider.

  • #react
  • #ssr
  • #security
  • #caching
  • #node-js

Related posts