The cache key is the whole game
6 min read
A cache sits between the user and the origin server and answers repeat requests itself. To decide whether two requests are "the same", it builds a cache key — usually the method, host and path, and maybe a query string or a header or two. Everything in the key distinguishes responses. Everything not in the key is invisible to the cache but may still change what the origin returns.
That gap is the entire attack surface. If an input is unkeyed but influential — the cache ignores it, the origin acts on it — you can send a request that the origin renders with your input baked in, and the cache will store that poisoned response and serve it to everyone who asks for that key.
The families
- Cache poisoning. Find an unkeyed header the origin reflects — classically
X-Forwarded-Hostreflected into an absolute URL, orX-Forwarded-Scheme. Poison the key once; every subsequent visitor gets your payload. - Cache deception. The reverse. Trick the cache into storing a
private page as if it were a static asset — request
/account/wallet.css; the origin ignores the suffix and serves the victim's account page, the cache sees ".css" and caches it publicly. Now you fetch it. - Key confusion / normalisation. The cache and origin disagree on what the path or parameters mean, so one request maps to two different views.
- Cache-hit side channels. A hit is fast and a miss is slow, and the response usually says which. That one bit leaks whether someone else already requested a resource — enough to infer what other users are doing.