When a WAF, gateway, proxy and application each parse the same request differently, a security check can approve one interpretation while the code acts on another. HTTP parameter pollution is the best-known case: send a parameter twice and one component reads the first value while another reads the last. The same class of flaw, the parser differential, appears with duplicate JSON keys, URL paths that normalise differently, multipart bodies that two libraries split differently, and large numbers that lose precision in one service but not the next. These bugs produce authorisation bypasses, filter evasion and data integrity failures that unit tests rarely catch, because each component behaves correctly on its own. This guide explains the main differential families, how to design so a single interpretation wins, how to test safely and what to monitor. It is for application security engineers, API and platform teams that run gateways and service meshes, and reviewers of multi-tier architectures.
Key takeaways
- The bug lives between components. OWASP's testing guide notes that duplicate parameters are not a vulnerability in themselves; the risk is two components that resolve them differently, such as a security check reading the first value and the action reading the second.
- Standards leave room for disagreement. RFC 8259 says JSON object names SHOULD be unique and that behaviour with duplicates is unpredictable; RFC 9110 lets recipients combine repeated header fields; the WHATWG URL Standard exists partly because URL parsers disagreed.
- Reject ambiguity at the edge. Refusing duplicate parameters, duplicate JSON keys, malformed encodings and conflicting headers is simpler and safer than trying to match every downstream parser.
- Decide once and pass the decision on. Parse and authorise in one place, then forward a canonical form, rather than letting each hop re-parse the raw request.
- ASVS 5.0 makes this testable. V1.5.3 requires consistent parsing across parsers for the same data type, V15.3.7 requires defences against HTTP parameter pollution, and V4.2.1 requires consistent HTTP message boundaries.
Why parser differentials happen
A modern request crosses a CDN, a WAF, a load balancer, an API gateway, a service mesh sidecar and one or more application frameworks, often written in different languages. Each layer parses the URL, headers and body to make its own decisions: routing, rate limiting, access control, caching, input filtering. When their parsers disagree on an ambiguous input, the attacker chooses the interpretation each layer sees.
| Ambiguity | What the standard says | Where components diverge |
|---|---|---|
| Repeated query or form parameter | No single rule for application frameworks | First value, last value, comma-joined string or a list |
| Duplicate JSON key | RFC 8259 section 4: names SHOULD be unique; behaviour of receivers is unpredictable, and many implementations report only the last pair | First wins, last wins, error, or both kept |
| Large or precise numbers in JSON | RFC 8259 section 6: integers in the range [-(2^53)+1, (2^53)-1] are interoperable | Rounding, truncation to zero or a maximum, conversion to strings |
| Repeated header field | RFC 9110 section 5.3: senders must not repeat a field unless it is list-based, and recipients may combine lines with commas (Set-Cookie is the exception) | Some take the first, some the last, some join; joining X-Forwarded-For or Host changes meaning |
| URL syntax and normalisation | RFC 3986 and the WHATWG URL Standard, which aims to align with and obsolete RFC 3986 and 3987 | Backslashes, percent-encoded slashes, dot segments, path parameters, user information |
| Multipart bodies | RFC 7578: boundaries must not appear inside parts, filename* must not be used, Content-Transfer-Encoding should not be sent | Libraries differ on duplicate parts, quoted boundaries, header folding and filename parameters |
| Message boundaries | RFC 9112 and HTTP/2 framing rules | Content-Length versus Transfer-Encoding disagreements; see the HTTP Request Smuggling Guide |
| Character encoding | RFC 8259 section 8.1: UTF-8 is required for JSON exchanged outside closed ecosystems | Invalid surrogates, overlong forms and byte order marks handled differently |
HTTP parameter pollution
OWASP's Web Security Testing Guide documents typical default behaviour for color=red&color=blue. Defaults can change with configuration, middleware and library versions, so always confirm against your own stack.
| Stack (OWASP WSTG) | Typical default for duplicates |
|---|---|
| ASP.NET on IIS | Values joined with a comma |
| ASP.NET Core on Kestrel | All values kept; comma-joined when converted to a string |
| PHP (Apache, php-fpm, NGINX) | Last value used |
| Java Servlet containers (Tomcat, Jetty) | First value from getParameter |
| Python frameworks (Django, Flask) | All values kept in a list or MultiDict; the single-value accessor picks one |
| Node.js frameworks | Depends on the query parser and its settings; some return arrays, which changes the type the code receives |
How HPP becomes a vulnerability
- Authorisation split: the WSTG cites a Blogger case in which the security check used the first
blogIDand the action used the second, so a user passed the check for their own blog and acted on someone else's. - Filter evasion: a WAF inspects each parameter value separately while the application concatenates them, so a blocked string split across two copies of the same parameter is never seen whole. The WSTG gives a ModSecurity example.
- Server-side request building: the application inserts user input into a query string it sends to a backend API; an encoded
&in the input adds or overrides parameters in the backend call, such as an account identifier or an action. This is where HPP meets SSRF; see the SSRF Prevention Guide. - Client-side HPP: injected parameters appear in links, form actions or
fetchURLs the page builds, altering requests the user's browser later sends. - Source confusion: frameworks that merge query, body and cookie parameters into one collection let a value from one source override another. ASVS 5.0 V15.3.7 calls this out specifically.
- Type confusion: a parser that returns an array where code expects a string can bypass equality checks or crash validation. See the TypeScript and JavaScript Secure Coding Guide.
Defences
- Reject duplicates for single-valued parameters at the gateway and in the application, returning 400, unless the API contract explicitly defines a list.
- Read parameters from a declared source (query or body, not a merged collection) and bind them to typed models that fail on arrays where scalars are expected.
- Build outbound URLs with a URL builder that encodes each parameter value, never by string concatenation. ASVS 5.0 V1.2.2 requires context-appropriate encoding when building URLs.
- Make the WAF and the application agree: configure the WAF to inspect the combined value, or better, to block duplicate single-valued parameters outright. See the Web Application Firewall Guide.
JSON interoperability
Research published by Bishop Fox in 2021 showed that popular JSON parsers disagree on duplicate keys, on keys containing invalid Unicode that some parsers truncate (so two different keys collide in one parser but not another), on non-standard comments, on large numbers and on trailing data after the document. RFC 8259 allows parsers to accept non-JSON extensions, so permissive behaviour is not a bug in the parser's own terms.
| Differential | Example consequence | Defence |
|---|---|---|
| Duplicate keys | A gateway validates "role": "user" while the service reads a second "role" key | Configure parsers to reject duplicate keys; reject at the edge |
| Key collision through truncation | A key that looks different to the validator becomes role after a lenient parser drops invalid characters | Reject invalid Unicode or replace it with U+FFFD consistently; never truncate |
| Comments and trailing data | One parser ignores text the other treats as keys; trailing data allows unexpected content types to parse as JSON | Use strict RFC 8259 parsing modes; require Content-Type: application/json |
| Large numbers | A quantity or price is rounded differently in the pricing and fulfilment services | Send identifiers and money as strings or fixed-point integers; reject numbers outside the interoperable range |
| Re-serialisation | A proxy parses, modifies and re-serialises a body, preserving duplicates that the next hop reads differently | Forward the validated, canonical object, not the original bytes |
The same research makes an important point for defenders: schema validators only see the object a parser has already produced, so a JSON Schema check cannot catch a duplicate key that the parser silently discarded. Duplicate detection has to happen in the parser. See the API Security Guide.
URL and path differentials
Routing and authorisation decisions often depend on the path. If the gateway that enforces "only admins may reach /admin" normalises the path differently from the application that routes it, an encoded or unusual form of the path can skip the check and still reach the handler.
- Backslashes: the WHATWG URL Standard treats a backslash in special schemes such as
httpsas a validation error and parses it as a slash; RFC 3986 parsers and many server-side libraries do not. Validation of redirect targets and SSRF allowlists is a common victim. - Encoded separators and dot segments: percent-encoded slashes and dots, double encoding and
..segments may be decoded and resolved by one component but not another. ASVS 5.0 V1.1.1 requires decoding into canonical form once, before validation. - Path parameters and suffixes: some Java servers strip
;paramsegments from paths while proxies keep them, and some frameworks match routes with optional trailing slashes or extensions. - Case and Unicode: case-insensitive file systems and Unicode normalisation can make two distinct strings map to the same resource. RFC 9110 section 4.2.3 says scheme and host are case-insensitive while other components are compared case-sensitively.
- User information: RFC 9110 deprecates
user:password@in http and https URIs and advises treating it as an error in untrusted input, because it confuses humans and parsers about the real host.
Defences: canonicalise once at the edge and forward the canonical path; reject requests containing encoded slashes, backslashes or dot segments unless a route needs them; enforce authorisation in the application on the resolved route, not only on the raw path in the gateway; and use one URL parsing library for all allowlist decisions in a service. See the Path Traversal Guide and the Open Redirect Guide.
Headers, multipart and message boundaries
- Repeated security headers: two
Host,AuthorizationorContent-Typefields should be rejected. For client IP headers, ASVS 5.0 V15.3.4 requires that proxies pass the original IP in trusted fields the user cannot manipulate; configure each proxy to overwrite, not append to, the header the application trusts. See the Proxies and Forward vs Reverse Proxy Security Guide. - Invalid characters: RFC 9110 section 5.5 requires recipients to reject or replace CR, LF and NUL in field values. A component that passes them through can enable header injection downstream.
- Multipart: WAFs and frameworks can disagree about duplicate part names, quoted versus unquoted boundaries, a boundary appearing inside a part, and
filenameversus the forbiddenfilename*parameter. Reject malformed multipart bodies, use one well-maintained parser, and treat filenames as untrusted, ignoring path components as RFC 7578 advises. See the Secure File Upload Handling Guide. - Content type confusion: a WAF that inspects JSON only when
Content-Typeis exactlyapplication/jsonmay skip a body that the framework parses as JSON regardless of a charset parameter or different casing. Require and validate the content type, and make the WAF apply the same body parser. - Message framing: ASVS 5.0 V4.2.1 requires every component to determine message boundaries correctly for the HTTP version, and V4.2.3 forbids connection-specific headers such as
Transfer-Encodingin HTTP/2 and HTTP/3. These are the controls for request smuggling, the most severe parser differential.
Designing for a single interpretation
- Map the chain: list every component that parses requests for each public route (CDN, WAF, load balancer, gateway, sidecar, framework, any service that re-parses) and the library and version each uses.
- Set strict edge rules: at the first component you control, reject duplicate single-valued parameters, duplicate JSON keys, invalid UTF-8, encoded path separators, conflicting or repeated security headers and malformed multipart bodies. Run the rules in log-only mode for one to two weeks and review what legitimate clients send.
- Canonicalise and forward: have the edge forward a normalised request, and where a gateway validates a token or body, pass the validated claims or object downstream in a signed or trusted internal header rather than expecting the service to re-derive them.
- Authorise where the action happens: keep object-level and function-level checks in the service that performs the action, using the same parsed values it acts on. See the Broken Access Control Prevention Guide.
- Standardise parsers: one JSON library configured for strict mode per language, one URL library, and shared request-binding middleware across services. See the Microservices Security Guide.
- Enforce and roll back safely: switch rules to blocking route by route, with a per-rule kill switch so a partner integration that sends duplicates can be exempted temporarily while it is fixed.
Small versus large organisations: a single application behind one reverse proxy can usually get most of the benefit from framework settings plus strict proxy rules. Large estates with several gateways and languages need a parser inventory and a shared differential test suite run in CI against every component.
Testing and detection
- Differential test suite: in an authorised test environment, send the same crafted but benign inputs (duplicate parameters, duplicate keys, alternative path encodings, repeated headers) through the full chain and directly to the application, and compare which value each layer acted on. The WSTG suggests comparing a baseline request, a tampered request and a request carrying both values.
- Manual review over automation: the WSTG notes that exploitability depends on business logic and that automated scanners produce many false positives, so focus testing on authorisation, pricing and routing decisions.
- Code review: look for merged parameter collections, single-value accessors on parameters that can repeat, string-built outbound URLs, and lenient JSON parser settings. See the Secure Code Review Guide.
- Logging: log rejected requests with the rule that fired (duplicate key, duplicate parameter, encoded separator) at the edge, and alert on clusters from one client.
Metrics:
- Share of public routes with a documented parser chain.
- Edge rejections per rule per day, and the share later confirmed as legitimate clients (should fall after rollout).
- Number of services using strict JSON parsing with duplicate-key rejection.
- Differential test cases in CI and the number of components covered.
- Open findings where gateway and service parse differently (target zero).
Common failure patterns
- Gateway authorises, service decides: the gateway checks an
accountIdfrom the first parameter or JSON key, and the service uses the last one. - WAF as the only defence: injection filtering relies on a WAF whose parser differs from the framework, so split or duplicated values pass.
- String-built backend URLs: user input concatenated into an internal API call, letting an encoded
&override parameters the user should not control. - Appending proxies: each proxy appends to
X-Forwarded-Forand the application trusts the first entry, which the client supplied, for rate limiting and audit. - Path-only gateway rules: admin routes protected by a gateway path match that an alternative encoding of the path avoids, with no check in the application.
- Schema validation trusted for duplicates: teams assume JSON Schema rejects duplicate keys, but the parser has already discarded one.
Framework mapping
| Control | OWASP ASVS 5.0 | MITRE CAPEC | NIST SP 800-53 Rev. 5 |
|---|---|---|---|
| Reject or safely handle duplicate parameters | V15.3.7 | CAPEC-460 HTTP Parameter Pollution | SI-10 |
| Consistent parsing across parsers | V1.5.3 | CAPEC-267 Leverage Alternate Encoding | SI-10 |
| Single canonical decoding before validation | V1.1.1 | CAPEC-267 | SI-10 |
| Consistent message boundaries | V4.2.1, V4.2.3 | CAPEC-33 HTTP Request Smuggling | SC-7 |
| Trusted client IP propagation | V15.3.4 | n/a | SC-7, AU-2 |
| Edge rejection logging | n/a | n/a | SI-4 |
In NIST CSF 2.0 these practices fall under PR.PS-06 (secure software development) and PR.IR-01 (protecting environments from unauthorised logical access); ISO/IEC 27001:2022 Annex A control A.8.28 Secure coding applies.
Practitioner checklist
- Every public route has a documented chain of parsing components with library versions.
- The edge rejects duplicate single-valued parameters, duplicate JSON keys, invalid UTF-8 and encoded path separators.
- Applications read parameters from a declared source and bind them to typed models that reject arrays for scalars.
- JSON parsers run in strict mode with duplicate-key rejection; identifiers and money travel as strings.
- Outbound URLs are built with a URL builder, never by concatenation.
- Authorisation runs in the service on the same parsed values it acts on, not only in the gateway.
- Proxies overwrite the client IP header the application trusts.
- Repeated
Host,AuthorizationandContent-Typeheaders and malformed multipart bodies are rejected. - A differential test suite runs in CI against the full chain and the application directly.
Standards and references
- OWASP: Application Security Verification Standard 5.0
- OWASP: Web Security Testing Guide, Testing for HTTP Parameter Pollution
- IETF: RFC 8259 The JavaScript Object Notation (JSON) Data Interchange Format
- IETF: RFC 9110 HTTP Semantics
- IETF: RFC 7578 Returning Values from Forms: multipart/form-data
- WHATWG: URL Standard
- MITRE CAPEC: HTTP Parameter Pollution (HPP)
- Bishop Fox: An Exploration of JSON Interoperability Vulnerabilities
Related NHI Mgmt Group resources: HTTP Request Smuggling Guide · API Gateway Security Guide · Web Application Firewall Guide · Input Validation and Output Encoding Guide