Web cache deception
When it applies
A caching layer (CDN, reverse proxy) fronts an app that returns per-user content, and the cache decides what to store by URL pattern/extension. If you can make an authenticated response look cacheable, the cache stores it — and anyone can then fetch that victim's data.
Why it works
The cache and the origin disagree about the URL. The cache sees /account.css and thinks "static,
cache it"; the origin ignores the .css suffix (or a delimiter) and serves the dynamic
/account page. The victim's private response gets stored under a public key you can request.
Method
- Confirm the mismatch: request an authenticated page with a static suffix —
/account/foo.css,/account%0a.css,/account;foo.js,/account%2ffoo.css. If the origin still returns the account page (not 404), the origin is ignoring the suffix. - Confirm caching: repeat and watch
X-Cache/CF-Cache-Statusflip toHIT, or aAgeheader appear. A cached, authenticated response is the vulnerability. - Prove cross-user impact: as the victim, visit the crafted URL once (or have them); then as an unauthenticated attacker request the same URL and receive the victim's private data.
- Explore delimiters/normalisation: caches and origins normalise
;,%2f,//,.., trailing dots differently — Param Miner's rules or manual fuzzing find the pair that splits them. - Report with least data: prove one private field leaked; don't hoard victim data.
Gotchas
- This is not cache poisoning: deception caches a victim's real response; poisoning injects a
malicious response via unkeyed input. Different bug, different report (
web-cache-poisoning). - Needs a genuinely cacheable-looking key and an origin that ignores it — confirm both.
- Some CDNs cache by content-type, not extension — test path-based tricks too.
Verify success
An unauthenticated request retrieves another user's authenticated content from the cache
(HIT/Age present), reproducibly.
References
Omer Gil "Web Cache Deception"; PortSwigger web cache deception labs; CDN caching-rule docs.