# Cache key collision via URL normalization

Cache key built from decoded or truncated input, collapsing distinct requests into one entry

_Area: Web infrastructure and server-side requests. Seen in 4 disclosed reports; 2 confirmed bounties: $1,120 and $1,250; latest 2025-11-18. Updated 2026-10-23._

## How it works
A cache decides whether two requests are "the same" by computing a key. If that key is derived from a normalized, decoded, or truncated form of the request rather than the raw request, two genuinely different requests can produce the same key. The second request then receives the first request's cached response. The broken assumption is that the key faithfully represents the request; normalization silently discards the differences that mattered.

## Where it shows up
- Cache key computed from a decoded URL, especially userinfo or percent-encoded delimiters [E2](https://bugbountylibrary.com/r/h1%3A824753.md)
- Cache key that includes a per-request parameter (JSONP callback name, index) that the attacker controls [E3](https://bugbountylibrary.com/r/h1%3A263760.md)
- Proxy or gateway with a documented header-count or size truncation limit [E1](https://bugbountylibrary.com/r/h1%3A3027461.md)
- Security-relevant headers (cache rules, WAF inputs) evaluated after a truncation boundary [E1](https://bugbountylibrary.com/r/h1%3A3027461.md)
- Responses cached despite no-cache headers [E3](https://bugbountylibrary.com/r/h1%3A263760.md)

## How to review
Find where the cache key is computed and confirm it is built from the raw, unmodified request (raw URL bytes, full header set), not a decoded or truncated derivative. The invariant: any two requests that differ in a security-relevant way must produce different keys. Check that normalization happens only after keying, and that truncation limits are below any attacker-reachable count or that truncated fields are never used for keying or WAF decisions. For parameterized keys, confirm attacker-controlled values cannot select another user's cached entry.

## Variants seen
- Decoded userinfo in URL collapses distinct origins into one key [E2](https://bugbountylibrary.com/r/h1%3A824753.md)
- Header-count overflow drops keyed headers from cache and WAF evaluation [E1](https://bugbountylibrary.com/r/h1%3A3027461.md)
- Varying JSONP callback name enumerates stale cached snapshots of later-private data [E3](https://bugbountylibrary.com/r/h1%3A263760.md)

## Why fixes fail
E3 shows caching continued despite no-cache headers, so header-based opt-outs alone did not prevent stale snapshots. E1 shows a documented truncation field existed but its security consequence (dropped keyed headers) was not addressed.

## Impact and severity
Led to cache poisoning and stored XSS with WAF bypass [E1](https://bugbountylibrary.com/r/h1%3A3027461.md), cross-domain response substitution [E2](https://bugbountylibrary.com/r/h1%3A824753.md), and disclosure of tweets later made private or deleted [E3](https://bugbountylibrary.com/r/h1%3A263760.md). Severity rises when the collision crosses trust boundaries (origins, users) or bypasses a WAF, and when stale private data remains retrievable long after deletion.

## Evidence
- E1: https://bugbountylibrary.com/r/h1%3A3027461.md
- E2: https://bugbountylibrary.com/r/h1%3A824753.md
- E3: https://bugbountylibrary.com/r/h1%3A263760.md
