Web cache deception
TL;DR: Tricking the cache into storing a sensitive personalised response under a public-looking path.
What it is
A confused-deputy between an origin that dynamically routes paths and a CDN that decides cacheability by suffix. The attacker convinces the victim’s browser (or a crawler) to fetch a sensitive URL with an appended *.css/*.js/*.png segment. The origin still returns the personalised page; the cache sees a “static” extension and stores it. Any subsequent attacker request to the same path retrieves the victim’s response.
Preconditions / where it applies
- A CDN/reverse cache in front of the origin (Cloudflare, Akamai, Fastly, Varnish, nginx with default extension caching)
- Origin that ignores or rewrites trailing path segments (PHP
PATH_INFO, Rails route globbing, IIS?handling, Express trailing slashes) - A way to get the victim to issue the malicious URL while authenticated (link, image-src, cross-site-scripting, open-redirect)
Technique
- Identify a personalised endpoint, e.g.
/accountreturning the victim’s profile JSON/HTML. - Find the cache rule. Often “cache by extension” —
.css,.js,.gif,.ico,.svg,.woff. - Probe the origin’s path handling. Try variants:
1 2 3 4 5 6 7
/account/foo.css /account/foo.js /account;name=foo.css /account%2ffoo.css /account%00foo.css /account?x=.css /account/.css
If
/account/foo.cssreturns the victim’s personalised HTML (HTTP 200, account body), the origin ignored the suffix. - Confirm caching: send the same URL twice unauthenticated; second response shows the cached private body and a cache HIT header (
CF-Cache-Status: HIT,X-Cache: HIT,Age:non-zero). - Lure the victim —
<img src="https://target/account/foo.png">in a third-party page is enough; the browser sends cookies, origin renders private content, cache stores under public key. - Visit the URL from any client to read the cached PII / auth tokens / CSRF token.
- Omer Gil’s “delimiter” variants extend this to non-extension delimiters (
/,;,\, encoded slashes) where origin and cache disagree on path normalisation.
Detection and defence
- Cache key on full normalised path as the origin sees it — share a parser between proxy and origin where possible.
- Never cache responses with
Set-Cookie,Authorizationrequest header, orCache-Control: private— enforce at CDN. - Strip or canonicalise unexpected suffixes at the edge before routing.
- Add
Cache-Control: no-storeto anything personalised; verify withcurl -sIafter deploy. - Hunt logs for high HIT ratios on URLs with extensions that mismatch their content-type (e.g.
text/htmlserved as*.css). - Related: cache-poisoning, http-request-smuggling, canonicalization-attacks.
Technique — variant: cache key normalisation
Some CDNs lower-case the cache key. /Account/foo.css and /account/foo.css map to the same key but route to different controllers — a deception variant without needing an extension.
References
- Omer Gil — Web Cache Deception Attack — original write-up
- PortSwigger — web cache deception — academy lab and theory
- Akamai — WCD revisited — delimiter variants at scale