Web Cache Deception
Web cache deception: tricking caches into storing authenticated responses at attacker-accessible URLs using static extension confusion, path delimiter differences, and URL normalisation discrepancies. Detection and exploitation workflow.
What is Web Cache Deception
Web cache deception is the opposite of cache poisoning:
- Cache poisoning: attacker causes the cache to serve a malicious response to other users.
- Cache deception: attacker causes the cache to store a sensitive authenticated response and then retrieves it as an unauthenticated user.
The attack exploits a discrepancy between how the cache decides what to cache (often based on URL/extension) and how the application decides what to serve (often based on session/routing logic).
Detection
Step 1 — Find authenticated pages that serve personal data
Look for pages that return personal information only when logged in:
/account/profile
/settings/email
/dashboard
/api/user/me
Step 2 — Append a fake static path
Add a path segment with a static file extension that the cache is configured to store:
/account/profile/x.css
/account/profile/x.js
/account/profile/x.png
/settings/email/foo.jpg
Send this request while authenticated. Check the response:
- Does it still serve your profile data? (application ignores the appended segment)
- Does the response contain
Cache-Control: publicorAge:header? (cache stored it) - Is
X-Cache: HITin the response?
Step 3 — Repeat unauthenticated
Log out (or use a different browser/incognito), and request the same crafted URL:
GET /account/profile/x.css
If you receive the victim’s profile data → cache deception confirmed.
Exploit
Full attack flow
-
Attacker (while unauthenticated) crafts a URL with static extension:
https://TARGET/account/profile/evil.js -
Attacker sends the link to the victim (phishing, injected href, etc.).
-
Victim (authenticated) clicks the link → their browser requests
/account/profile/evil.js. -
Application serves the victim’s profile (ignores
/evil.js, routes to/account/profile). -
Cache sees
.jsextension → caches the response at/account/profile/evil.js. -
Attacker requests
/account/profile/evil.jsunauthenticated → receives the victim’s cached profile data (tokens, email, CSRF token, PII).
Bypass Techniques
Path delimiter discrepancies
Different servers handle path delimiters differently — try:
/account/profile%2Fevil.js
/account/profile;evil.js
/account/profile.evil.js
/account/profile%09evil.js
The application routes to /account/profile, the cache keys on the full URL including the delimiter.
URL normalisation mismatch
/account/profile/..%2F..%2Fevil.js
/account/%2e%2e/profile/evil.js
If the cache normalises the URL before storing (resolves ..%2F) but the app doesn’t → different cache key vs app routing.
Query string caching
Some caches ignore query strings for caching decisions:
/account/profile?cb=evil.js
What Data Can Be Cached
Anything in the authenticated response:
- Session tokens / CSRF tokens (use them to take over the account)
- API keys
- Email addresses, PII
- Partial HTML including
<meta name="csrf-token" content="...">
If the cached response contains a CSRF token → use it to perform state-changing actions as the victim.
Burp Suite workflow
- Proxy — browse the application while authenticated; identify personal data pages.
- Repeater — test crafted URLs with static extensions; compare authenticated vs unauthenticated responses.
- Param Miner — use the Web Cache Deception scanner feature to automatically test cache deception across crawled endpoints.
- Check for
Age:,X-Cache: HIT,CF-Cache-Status: HIT,Surrogate-Keyresponse headers — these indicate a CDN/cache layer is active. - Use Collaborator to confirm which URLs trigger server-side caching by checking timing differences.