HTTP parameter pollution
When it applies
A request passes through more than one component that parses parameters — a WAF/gateway, then the app; or a frontend that builds a backend request. If they resolve a duplicated parameter differently, you can show one value to the guard and another to the logic.
Why it works
There is no single rule for ?x=a&x=b: PHP/Apache take the last, ASP/IIS concatenates
(a,b), classic JSP takes the first, Node/Express makes an array. When the validator and
the consumer sit on different stacks, a value that passes validation isn't the value that's used.
Method
- Map the parsing: send
?p=1&p=2(and body dups) and observe which value the response reflects or acts on — that tells you first/last/concat/array. - Split a blocked payload: if a WAF blocks
q=<svg>, tryq=<svg&q=onload=..>where the backend concatenates — the signature never appears whole to the WAF. - Override server-side params: append your own copy of a param the app also sets internally
(e.g.
role,amount,redirect_uri) so your last-wins value overrides the trusted one. - Access control / logic: pollute IDs or flags where the auth check reads one occurrence and the data layer reads another.
- Client-side HPP: when a link/form is built from your input, inject
&-encoded params to add fields to the generated request.
Gotchas
- Behaviour is stack-specific — always confirm the parsing empirically before relying on it.
- Body vs query vs path params may parse differently in the same app; test each channel.
- Concatenation (
a,b) can corrupt the payload — order the duplicates to land valid syntax.
Verify success
The duplicated parameter produces a different outcome than the single one — a filter is bypassed, an internal value overridden, or a logic/authz decision changes — reproducibly.
References
OWASP Testing Guide (HPP); PortSwigger notes on parameter parsing; framework parameter-precedence tables.