Nouvelle réglementation de cybersécurité 2026 en vigueur au Maroc. Obtenir un audit de conformité gratuit →
← Retour au blog
OWASP 2025-11-20 ⏱️ 11 min

Web Cache Deception in 2025: Path Confusion, Cache Rules, and the Limits of Deception Armor

Web Cache Deception in 2025: Path Confusion, Cache Rules, and the Limits of Deception Armor

Web cache deception (WCD) is an elegant attack. The attacker does not need to steal a session cookie, crack a password, or exploit memory corruption. They only need to make a shared cache store a private, authenticated response under a URL that they can later request without authentication. The victim visits a crafted link; the cache saves the page; the attacker reads it. The technique was popularized by Omer Gil in 2017 and studied at scale in the USENIX Security 2020 paper “Cached and Confused.” It remains relevant because the underlying disagreement — what the origin considers a dynamic page versus what the cache considers a static file — has never been fully resolved.


1. The Core Mechanism

Consider an application that serves a private account page at:

GET /account HTTP/1.1
Host: example.com
Cookie: session=victim-session

The origin returns HTML containing the victim's email, balance, or API key, with Cache-Control: private. A correct cache does not store it. Now consider a request with an extra path segment and a static-looking extension:

GET /account/avatar.css HTTP/1.1
Host: example.com
Cookie: session=victim-session

Many frameworks route this to the same account controller because the router ignores trailing segments or treats them as parameters. The origin still returns the private HTML. But the cache sees a .css path and applies a static-asset rule: cache it publicly, often for hours. The attacker then requests /account/avatar.css without a cookie and receives the victim's page.

The vulnerability is not in the cache or the application alone. It is in the mismatch between their normalization and routing rules.


2. Variants That Still Work

2.1 Extension confusion

Appending .css, .js, .png, .ico, or .woff2 to a dynamic route is the classic form. It works when the origin ignores the suffix but the CDN's cache rule matches it.

2.2 Path normalization differences

Caches and origins normalize .., ., duplicate slashes, semicolons, encoded slashes (%2f), and backslashes differently. A request such as /account/..%2fstatic/app.css may be routed by the origin to /account while the cache keys it as a static file. James Kettle's 2024 research on path confusion demonstrated how these discrepancies survive in major CDNs.

2.3 Delimiter and parameter tricks

Some origins treat ;, ?, or # as routing delimiters; caches may not. A URL like /account;.css can be interpreted as /account by the application and as a CSS file by the cache. Similarly, a cache may ignore a query string when building its key while the origin uses it to select content.

2.4 Header and method confusion

If the cache keys on method but the origin treats HEAD like GET, a HEAD request may populate a cache entry later served to GET. Vary handling can also create gaps: if the cache does not include Cookie in the key (which is normal for public content), an authenticated response stored under a static rule is served to everyone.


3. Why “Cache Deception Armor” Is Not a Complete Answer

Cloudflare's Cache Deception Armor (CDA) compares the response Content-Type with the file extension in the request. If a request ends in .css but the origin returns text/html, CDA refuses to cache. That is a meaningful control, but it is a heuristic:

  • It depends on correct origin headers. If the origin omits or mislabels Content-Type, the comparison fails open or closed unpredictably.
  • It does not cover every extension. Custom static rules and uncommon MIME types may not be mapped.
  • Path confusion can defeat the extension check. If the cache normalizes the path differently from the origin, the extension CDA sees may not be the extension the origin routed on.
  • It does not fix the origin. The private response is still being generated and returned with cacheable semantics; another cache layer or a misconfigured proxy may still store it.

CDA reduces the attack surface. It does not replace correct Cache-Control headers and strict routing.


4. Testing Methodology

Only test systems you are authorized to assess. Use two accounts: a victim and an attacker. The goal is to prove that the attacker can retrieve the victim's private data through a cache.

  1. Identify private pages. Account settings, dashboards, invoices, API keys, admin panels.
  2. Establish a baseline. Request the page as the victim and record the response headers: Cache-Control, Vary, Age, X-Cache, CF-Cache-Status, Akamai-Cache-Status.
  3. Append static suffixes. Try /account.css, /account.js, /account.png, /account/foo.css, and /account%2ffoo.css.
  4. Check cache status. A response that changes from BYPASS to HIT or MISS with a public cache header is promising.
  5. Request unauthenticated. From a clean session, request the same URL. If the victim's data appears, the cache stored a private response.
  6. Confirm with a second victim action. Change a value in the victim account and repeat to show the cached page updates or leaks the new value.
# Baseline as the victim
curl -s -D - -o /dev/null -b 'session=VICTIM' https://example.com/account

# Static-suffix probe
curl -s -D - -o /dev/null -b 'session=VICTIM' https://example.com/account/x.css

# Attacker retrieval without cookies
curl -s -D - https://example.com/account/x.css | head -40

5. Defensive Design

  • Mark authenticated responses explicitly. Send Cache-Control: private, no-store on every response that contains user-specific data. Do not rely on the absence of a public header.
  • Do not let static rules override origin semantics. Configure the CDN to cache only known static paths, not “anything ending in .css.” Prefer explicit allowlists over extension matching.
  • Normalize consistently. Ensure the edge and the origin agree on path decoding, slash handling, dot-segment removal, and delimiter parsing. Reject ambiguous paths at the edge.
  • Separate static and dynamic origins. Serving user content from app.example.com and assets from static.example.com removes most confusion. Cookies should be scoped to the application host, not the asset host.
  • Include the right dimensions in the cache key. If a response varies by cookie, authorization header, or tenant, either include it in the key or do not cache the response.
  • Test after every CDN rule change. Cache behavior is configuration, and configuration drifts. Add WCD probes to the regression suite.

6. Conclusion

Web cache deception exploits an ambiguity, not a broken cipher. The attacker asks the origin for one thing and the cache for another, and the gap between the two answers becomes a data leak. Extension checks and deception armor raise the cost, but the durable fix is to make the origin's privacy semantics explicit and to ensure the edge never caches a response it does not fully understand. If a page requires a session to render, its response must say so in Cache-Control — and the CDN must be configured to believe it.

Besoin d'un audit de sécurité ?

Contactez Cayvora pour une consultation gratuite et protégez votre entreprise contre les cybermenaces.

✉️ Contact via Email

Articles connexes