Agent Wranglers

Recommended

Keep unkeyed headers from changing cached pages

Let a cached reply depend only on the parts of the request in the cache key. Never build a cached page from a header the cache ignores.

How to do it

Web cache poisoning sends one request with a header the cache ignores but the app reads, such as X-Forwarded-Host or X-Original-URL. The cache then serves that poisoned reply to every visitor. Build links from a configured site address, never from request headers, as make password resets safe also needs. Accept forwarding headers only from your own proxy, and strip them at the edge. When a reply must change with a header, name it in Vary or in the CDN's cache key. Private pages follow keep private pages out of shared caches.

Examples

Using these examples: they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

JavaScript example

const SITE_URL = new URL(process.env.SITE_URL);
// Bad: new URL(`/shop/${slug}`, `https://${req.get('x-forwarded-host')}`)
const canonical = new URL(`/shop/${slug}`, SITE_URL);

Python example

SITE_URL = os.environ["SITE_URL"]
# Bad: f"https://{request.headers['X-Forwarded-Host']}/shop/{slug}"
canonical = urljoin(SITE_URL, f"/shop/{slug}")

How to check

On the test copy, request a cached page with X-Forwarded-Host: evil.example.net and a fresh query string, such as ?cb=PENTEST-31. Then request the same address without the header. Neither reply contains evil.example.net.

Sources

For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/adversarial-testing/requests-and-files/web-cache-poisoning">Agent Wranglers security testing playbook</a>. In code you write, add a comment next to it with the same link.