HTTP Parameter Pollution (HPP)
detect, understand, remediate
HTTP parameter pollution exploits the disagreement between WAFs, reverse proxies, and applications about which copy of a duplicated parameter to honour, enabling authorisation bypass, WAF evasion, redirect override, and rate limit evasion across the request path.
No credit card required. Free plan available forever.
What is HTTP parameter pollution?
HTTP parameter pollution (HPP) is a class of injection attack in which a request supplies the same parameter name more than once, and the server, proxy, web application firewall, and backend disagree about which value to honour. The HTTP specification does not mandate a single canonical behaviour for duplicate query string or form parameters, so each component along the request path picks its own rule: take the first, take the last, concatenate, prefer the longest, or surface them as an array. When two components on the same path apply different rules, the discrepancy becomes a security boundary the attacker can manipulate.
HPP is tracked under CWE-235 (improper handling of extra parameters) and adjacent CWE-444 (inconsistent interpretation of HTTP requests). It rarely produces an exploit on its own; its leverage comes from chaining a parser disagreement with a downstream control that trusted the wrong copy of the parameter. The same trick that defeats a WAF rule on the edge can also defeat an authorisation check at the application, a rate limit at the API gateway, or a logging filter at the proxy. Each of those chains turns a benign parser quirk into a real finding.
Like HTTP request smuggling and host header injection, HPP lives at the boundary between components that each behave correctly in isolation but combine into something exploitable. Treating it as a single application bug misses the point: HPP is almost always a configuration mismatch across the request path, and the remediation has to address every node in the chain that processes the request.
How it works
Identify a duplicated parameter
The attacker finds an endpoint where a query string or form parameter influences a control (auth check, filter, redirect target, item identifier, role flag) and submits the same parameter twice with different values.
Map the parser disagreement
The attacker observes which value each component honoured: the WAF logged one, the application acted on another, the proxy forwarded both. The discrepancy reveals which control trusted which copy of the parameter.
Craft the payload split
The attacker constructs a request where the value the WAF sees is benign but the value the application acts on is malicious, or vice versa. Common splits include role=user&role=admin, redirect=safe&redirect=evil, and id=1&id=2.
Trigger the chained behaviour
The benign value passes the gate; the malicious value reaches the action. The result can be authorisation bypass, parameter override, downstream injection on a sanitised value, or a WAF evasion that masks a separate finding.
How common platforms resolve duplicate parameters
The exploitability of HPP comes from the table below. Two components on the same request path that pick different rows produce a parser disagreement the attacker can exploit. Before remediation, every team should map this table for the stack actually in production rather than relying on framework defaults that may have been overridden.
| Component | Default behaviour | Notes |
|---|---|---|
| PHP | Last value wins | $_GET, $_POST overwrite earlier values; raw access via php://input shows all copies. |
| Node.js (qs / Express) | Array of values | Type changes from string to array, which often breaks downstream string-only checks. |
| ASP.NET | Comma-concatenated | Request["x"] returns "a,b"; downstream parsers may split on comma and pick either value. |
| Java (Servlet) | First value wins | getParameter() returns the first; getParameterValues() returns the full array. |
| Python (Flask / Django) | First value wins (via .get) | request.args.get returns the first; request.args.getlist returns the array. Mismatched access patterns inside one app are a classic HPP source. |
| Go (net/http) | First value wins (via Get) | r.URL.Query().Get returns the first; r.URL.Query()["k"] returns the slice. |
| Nginx / reverse proxies | Forward unchanged | Most reverse proxies pass duplicate parameters through verbatim, so the disagreement happens further down the path. |
| CDNs / WAFs | Vendor-specific | Some inspect only the first parameter, some only the last, some all. The exact rule must be confirmed per vendor and per ruleset version. |
Common causes
Mismatched parsers across the request path
The WAF, the reverse proxy, and the application each interpret duplicate parameters differently. The WAF inspects the first copy, the application acts on the last copy, and the parameter the security control trusted never reaches the action.
Mixed access patterns inside one application
The same parameter is read with .get in one handler and with .getlist in another. The two handlers see different values for the same request, and the difference becomes a privilege boundary an attacker can shift.
URL-encoded parameter names
The same logical parameter arrives under different encoded forms (id, %69d, %49d). Some parsers normalise; some do not. The disagreement creates a duplicate parameter scenario without a literal duplicate name.
Manual query string concatenation
Application code that appends user input into a downstream URL without re-encoding produces second-order HPP, where attacker-controlled parameters land in a request the application makes to a third-party API. The third-party parser then becomes the choke point.
Trusting framework auto-binding without verifying behaviour
Frameworks bind query strings to handler arguments under documented but easily forgotten rules. A handler that accepts a single string value receives whatever the framework picked from the duplicates, which is not always the value the developer intended.
Logging the wrong parameter copy
Access logs and audit trails may capture only the first or only the last value of a duplicated parameter. After-the-fact incident reconstruction reads the logged value, not the value the application acted on, and misattributes the actor.
How to detect it
Automated detection
- SecPortal's authenticated scanner tests duplicate parameter behaviour against discovered endpoints, comparing responses across first-wins, last-wins, and array variants.
- The external scanner inspects edge behaviour by sending duplicated parameters that should be flagged at the WAF and observing whether they reach the origin unchanged.
- Findings carry the request and response pair so the parser disagreement is reproducible against the asset under test, not just against the scanner's synthetic target.
Manual testing
- Resubmit a known-good request with the security-relevant parameter duplicated. Compare the response, log entries, and downstream side effects to the single-parameter baseline.
- Run targeted splits against authentication, authorisation, redirect, and identifier parameters: role, user_id, redirect_uri, return_to, callback, target, file, and any field bound to a server-side action.
- Test parameter pollution in body, header, and JSON forms in addition to query string. The same disagreement can apply to multipart fields and to repeated keys in JSON arrays where the application reads the first or the last.
- Walk every node along the request path: edge cache, WAF, reverse proxy, application, downstream service. Confirm the rule each one applies before recording a finding, not just the application's observable behaviour.
How to fix it
Reject duplicate parameters at the edge
Configure the WAF or reverse proxy to reject requests with duplicate security-relevant parameters before they reach the application. Choose rejection over normalisation: a 400 response is recoverable; a silent normalisation hides the parser disagreement that motivated the rule.
Pick one parser behaviour and enforce it
Decide explicitly whether the application uses first-wins, last-wins, or array semantics, document the choice, and verify that every handler reads the parameter the same way. Mixed access patterns inside one application are an HPP source even when no edge component is involved.
Sanitise on the canonical value, not the raw input
Apply input validation, escaping, and authorisation checks against the value the application will act on, not against the raw request. If the framework returns the first copy, validate the first; if the framework returns an array, validate every element.
Encode parameters when forwarding to downstream services
Application code that constructs requests to internal or third-party services from user input must re-encode each parameter individually rather than concatenating raw query strings. This prevents second-order HPP where attacker-controlled parameters reappear in a downstream request.
Log the parsed parameter, not the raw header
Access logs and audit trails should record the value the application acted on, with a marker that the original request carried duplicates. Logs that capture only the first or only the last copy mislead incident response.
Treat WAF and proxy upgrades as HPP regression points
Vendor parser behaviour can change between rule pack and engine releases. Add a duplicate-parameter test to the regression suite that runs after every WAF, CDN, or reverse proxy upgrade so behaviour drift surfaces before an attacker finds it.
Where HPP shows up in real engagements
HPP is most often surfaced as a contributing factor to a finding rather than as the finding itself. The chains below describe how a parser disagreement turns into something a client report needs to record. Recognising the chain at triage avoids burying the root cause behind a downstream symptom.
WAF bypass enabling SQL injection
A scanner reports SQL injection on a parameter the WAF claims it inspects. The bypass comes from a duplicate parameter where the WAF read the benign first copy and the application acted on the malicious second copy. The remediation lives in the WAF and the application, not just the database layer.
Redirect parameter override
An OAuth or SSO flow that takes a redirect_uri parameter accepts a duplicate where the first copy is on the allowlist and the second is attacker-controlled. The application returns the first to the authorisation server but actually redirects to the second after the round-trip.
Authorisation bypass on an identifier
A handler reads user_id with .get to enforce ownership, then reads it with .getlist in a separate code path that touches the same record. The attacker submits two values: the first owns the resource, the second targets a different account. Ownership passes; the action targets the wrong account.
Rate limit evasion
An API gateway throttles per token, but the token is read from the first copy of an Authorization parameter while the backend reads the last. The attacker rotates the second value to spread quota across multiple keys while the gateway sees a single steady client.
Reporting and triage in the engagement
HPP findings are easy to undersell on a client report because the proof-of-concept is a duplicated parameter that, on its own, looks harmless. The credible report explains the chain: which component honoured which value, what the security boundary was, and how the disagreement turned into impact. SecPortal's findings management stores the full request and response pair against the finding so the chain stays attached to the evidence rather than being reconstructed during retest.
Severity calibration matters here. Pure parser disagreement with no downstream impact is informational; a confirmed authorisation bypass or WAF evasion that hides a separate critical finding is high or critical, depending on what was hidden. The severity calibration research covers how to score chain findings against CVSS without double-counting the downstream issue. For the broader workflow that pairs scanner output with manual verification on these chains, the scanner false positive guide covers the triage discipline.
Retest planning has to cover every node in the chain. A fix that lands only in the application leaves the WAF and the proxy in their original state; the next vendor rule update or framework upgrade reintroduces the disagreement. The remediation tracking workflow keeps the chain attached to the retest scope so the verification covers the full request path rather than just the application change.
Compliance impact
Related vulnerabilities
Detect HTTP parameter pollution chains
SecPortal tests duplicate parameter behaviour against WAFs, proxies, and applications, captures the parser disagreement on the finding, and surfaces it in the client report. Start free.
No credit card required. Free plan available forever.