Cache Poisoning
debt(d7/e7/b5/t7)
Closest to 'only careful code review or runtime testing' (d7). The detection_hints indicate automated=no, and while tools like Burp Suite, Semgrep, and OWASP ZAP are listed, cache poisoning requires active probing and manual analysis to confirm — a Semgrep rule can flag reflected unkeyed headers but cannot confirm the cache behaviour. Silent in most CI pipelines, discovered only through dedicated security testing.
Closest to 'cross-cutting refactor across the codebase' (e7). The quick_fix sounds simple (never cache responses based on unvalidated headers; set Cache-Control: no-store on sensitive pages), but the common_mistakes reveal that fixes span caching infrastructure config, application-level header handling, URL generation logic, and separation of static vs dynamic responses. Correctly remedying cache poisoning requires auditing every cached endpoint, updating CDN/reverse proxy keying rules, and refactoring URL-generation code — a cross-cutting change.
Closest to 'persistent productivity tax' (b5). The applies_to covers web and api contexts broadly. Once cache poisoning risks are present, every feature that involves caching must be reviewed for unkeyed header reflection and response segmentation. It imposes an ongoing tax on caching design decisions across multiple work streams, but does not fully define the system's shape.
Closest to 'serious trap (contradicts how a similar concept works elsewhere)' (t7). The misconception field explicitly states that developers believe cache poisoning only affects public CDN caches, when in reality server-side application caches and reverse proxies are equally vulnerable. Combined with common mistakes like trusting X-Forwarded-Host and Host headers in cached responses, competent developers who haven't encountered this specific attack surface will systematically guess wrong about what constitutes a safe caching boundary.
Also Known As
TL;DR
Explanation
Web cache poisoning abuses the gap between what a caching layer uses as a cache key (typically URL + Host) and what the backend actually processes (including unkeyed headers like X-Forwarded-Host). By injecting a malicious value into an unkeyed header that the application reflects in its response, an attacker can poison the cached copy served to all subsequent visitors. Mitigations include keying caches on all relevant headers, disabling reflection of arbitrary headers, and using Vary headers appropriately.
How It's Exploited
Host: evil.com
# If Host is reflected in cached response, every subsequent visitor gets evil.com links
Common Misconception
Why It Matters
Common Mistakes
- Reflecting unkeyed request headers (like X-Forwarded-Host or X-Original-URL) into cached responses.
- Including user-controlled query parameters in responses that are served from cache to other users.
- Not separating cached static resources from dynamic personalised responses.
- Trusting the Host header for generating absolute URLs in cached pages.
Code Examples
// Caching a response that includes unvalidated Host header
$cacheKey = 'page:' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
$cached = $cache->get($cacheKey);
if (!$cached) {
$html = render(['canonical' => 'https://' . $_SERVER['HTTP_HOST'] . '/page']);
$cache->set($cacheKey, $html);
}
// Use a fixed canonical hostname — never trust the Host header for cache keys
$canonicalHost = config('app.url'); // 'https://yourapp.com'
$cacheKey = 'page:' . md5($canonicalHost . $_SERVER['REQUEST_URI']);
// In nginx — only forward known Host values to PHP:
// if ($host !~* ^(yourapp\.com|www\.yourapp\.com)\$) { return 444; }