HTTP Caching & Header Flaws
A private page cached publicly, cache poisoning through an unkeyed header, and cache deception that stores a private page under a static path.
- Track
- Web security
- Estimated time
- 22 min
- Level
- Intermediate
Before this lesson
Between the browser and the server sit shared caches — CDNs and proxies — that store responses to serve them faster to the next person. That sharing is the risk: if the wrong response is stored, under the wrong key, or served to the wrong user, caching turns a small header mistake into a leak that hits everyone.
What is the vulnerability?
- A private page cached publicly. A per-user page carries
Cache-Control: public, so the shared cache stores one user’s response and hands it to the next. - Cache poisoning. A page reflects an unkeyed input — a header like
X-Forwarded-Host— but the cache keys only on the path. One malicious request poisons the stored copy every later visitor receives. - Cache deception. A private page is
no-store, but the cache stores anything ending in.css, and loose routing returns the private page for/account/x.css. The victim’s data ends up cached under a static-looking path the attacker reads.
Why does it happen?
The cache and the application disagree about what a response depends on and whether it is private. The cache keys on the URL; the response varies on a header. The cache trusts the file extension; the application marked it no-store. Each gap is invisible until a shared cache sits in the middle.
How do you stop it?
Mark private responses Cache-Control: private, no-store. Never reflect an untrusted header into a cacheable response, and include everything that affects a response in the cache key. Make the cache honour caching headers rather than guessing from the path, and tighten routing so a crafted suffix cannot reach a sensitive handler.
The three labs below walk this: read a publicly-cached private page, poison a page through a header, and deceive the cache into storing a private page.
Labs
A private page in a public cache
SolvedEasyBrowser lab
Fetch admin private data from the shared cache.
Enter the labLockedCache poisoning
SolvedMediumBrowser lab
Poison the cache so the victim receives a script from your site.
Enter the labLockedCache deception
SolvedHardBrowser lab
Deceive the cache into storing admin data under a static path, then read it.
Enter the labLocked
