Web Cache Deception
Poisoning writes your content into a shared entry. Deception reads someone else’s private entry. Different bug, same cache.
The test
- Log in, open a page with your own data —
/account/me - Append a static-looking suffix —
/account/me/x.css - Same private data back? The origin ignores the extra path.
- Fetch the same URL in a private window with no cookies.
- Your data comes back → deception.
Check X-Cache: HIT, CF-Cache-Status: HIT, Age: to confirm it was cached and not just served.
Suffix set
/account/me/x.css /account/me/x.js /account/me/x.jpg
/account/me/x.png /account/me/x.svg /account/me/x.ico
/account/me%0a.css /account/me%3fx.css /account/me%23x.css
/account/me;x.css /account/me?x.css
/account/me/..%2fx.cssDelimiters matter more than the extension — the origin and the cache have to disagree about where the path ends.
; ? # %00 %0a %0d %09 . ..Static directory confusion
Some CDNs cache by prefix, not extension:
/static/..%2f..%2faccount/me
/assets/..;/account/meWhere the payoff is
- API tokens rendered into the page
- CSRF tokens (cached one → CSRF against everyone)
- Session identifiers in HTML
- PII on a profile / settings / invoice page
/api/me,/api/session,/graphqlresponses
Don’t cache your own bug
Test with a unique cache buster so you don’t poison a real entry:
/account/me/caon-<random>.cssIf you cache real user data by accident, say so in the report.
Tools
| URL | Description |
|---|---|
| https://github.com/PortSwigger/param-miner | Finds unkeyed inputs and delimiters |
| https://github.com/Hackmanit/Web-Cache-Vulnerability-Scanner | WCVS |