Web Cache Poisoning vs Cache Deception for Bug Bounty
Caches sit between users and origin. When they key responses poorly, one crafted request can stick for everyone — or trick the cache into storing a private page under a public URL.
Two related bugs get mixed up constantly. Poisoning changes what the cache stores for a shared key. Deception abuses path or extension quirks so a personal response gets cached as if it were static.
Cache poisoning in practice
You need an unkeyed input: a header or cookie the origin uses to build the page, but the CDN ignores when deciding the cache key.
Classic probes:
X-Forwarded-Host,X-Forwarded-Scheme,X-Original-URL- Fat GET bodies some stacks still honor
- Query parameters excluded from the key but reflected in HTML or redirects
Workflow: send a request that injects a harmless marker (unique subdomain, script source) via the unkeyed input, then fetch the same cache key without that input. If the marker appears for the second client, you poisoned the shared entry.
Vary Cache-Control / Age / X-Cache headers while you work. A HIT after your poison request is the money screenshot.
Web cache deception
Here the trick is path normalization. You request something like /account/settings/random.css. The origin may still return the authenticated HTML for /account/settings (route fallback), while the cache keys on the .css suffix and stores that HTML as a public static object.
Later, anyone who requests that path receives your private page — or you request a victim’s poisoned static path if you can coerce them to visit first, depending on the exact model.
Test ideas:
- Append junk segments and static extensions to sensitive paths.
- Compare responses authenticated vs anonymous for the deceptive URL.
- Check whether the deceptive URL becomes a cache HIT for a second session.
Sorting severity
Poisoning that injects XSS or steals tokens from many users is usually Critical/High. Deception that exposes one user’s PII under a guessable or attacker-controlled path is also High when you can demonstrate retrieval without their cookies.
Weak findings: reflections that never cache, or keys so unique nobody else will hit them.
Safe hunting tips
Use unique canaries. Prefer short TTLs on labs. Avoid flooding CDNs with megabyte payloads. Clear or wait out poisoned keys when the program asks you to clean up.
PortSwigger’s labs remain the best place to feel the difference between keyed and unkeyed inputs before you touch a live CDN. On real targets, map the cache layer first — Cloudflare, Fastly, Akamai, and app-level caches fail in different ways — then hunt for the mismatch between “what origin trusts” and “what the cache keys.”