shared × unstructured — bad data written permanently, and it spreads
Web Cache Poisoning
An input the cache key ignores gets baked into a stored response and replayed to everyone.
01the recipe
In the wild
compound ofCache InvalidationUnconstrained InputsCWE-348 Less Trusted SourcecompoundCWE-349 Extraneous Data
example.py
# SMELL: cache a response keyed on trusted parts, ignoring an input that shapes it.
# (cache invalidation x unconstrained inputs)
key = request.path # ignores the X-Forwarded-Host header...
cache[key] = render(page, host=request.headers["X-Forwarded-Host"])
# attacker sets the header once -> poisoned page served to everyone after.
# RIGHT: key on every input that affects the output, or don't trust it at all.
host = allowlist(request.headers.get("X-Forwarded-Host")) # vetted
cache[(request.path, host)] = render(page, host=host)An attacker-influenced input the cache key ignores gets stored in a response and replayed to every subsequent user. One request poisons the shared cache; the blast radius is everyone downstream.
// observed
poisoned: one crafted header rewrites the page for all users keyed: host vetted and part of the key -- no shared poisoning
02weakness catalog
Mapped weaknesses (CWE)
On its own, this defect is catalogued by MITRE as one or more of these weaknesses. The exploitable vulnerability usually appears only when it chains or combines with another.