You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
|**Requirement**| SHOULD reject, MAY parse leniently |
13
+
|**Expected**|`400` or `2xx`|
14
14
15
15
## What it sends
16
16
@@ -29,10 +29,19 @@ Note the double space between `GET` and `/`.
29
29
30
30
The request-line grammar is `method SP request-target SP HTTP-version CRLF` where `SP` is exactly one space. Multiple spaces do not match this grammar, making the request-line invalid. Recipients SHOULD respond with 400.
31
31
32
+
However, RFC 9112 §3 also states:
33
+
34
+
> "Although the request-line grammar rule requires that each of the component elements be separated by a single SP octet, recipients MAY instead parse on whitespace-delimited word boundaries."
35
+
36
+
This means a server that collapses multiple spaces and processes the request is also RFC-compliant.
37
+
38
+
**Pass:** Server rejects with `400` (strict, follows SHOULD).
39
+
**Warn:** Server accepts and responds `2xx` (RFC-valid per MAY parse leniently).
40
+
32
41
## Why it matters
33
42
34
43
Some parsers are lenient and collapse multiple spaces. If a front-end collapses spaces but a back-end does not, they may parse the method, target, or version differently — leading to routing confusion or bypass.
@@ -32,13 +31,20 @@ The chunk size line `5;` is terminated with bare LF (`\n`) instead of CRLF.
32
31
33
32
## What the RFC says
34
33
35
-
Chunk extensions must follow the grammar `chunk-ext = *( BWS ";" BWS chunk-ext-name [ "=" chunk-ext-val ] )` with CRLF terminating the chunk line. A bare LF in the extension area violates the line terminator requirement of RFC 9112 Section 2.2.
34
+
Chunk extensions must follow the grammar `chunk-ext = *( BWS ";" BWS chunk-ext-name [ "=" chunk-ext-val ] )` with CRLF terminating the chunk line. However, RFC 9112 §2.2 states:
35
+
36
+
> "Although the line terminator for the start-line and fields is the sequence CRLF, a recipient MAY recognize a single LF as a line terminator and ignore any preceding CR."
37
+
38
+
This means a server MAY accept bare LF — both strict rejection and lenient acceptance are RFC-compliant.
39
+
40
+
**Pass:** Server rejects with `400` (strict, safe).
41
+
**Warn:** Server accepts and responds `2xx` (RFC-valid per §2.2 MAY accept bare LF).
36
42
37
43
## Why it matters
38
44
39
45
This is the **TERM.EXT** vector from chunked encoding research. If a parser accepts bare LF in chunk extensions, it may parse chunk boundaries differently from a strict parser, enabling desynchronization and smuggling.
The CRLF after chunk data is mandatory. A bare LF violates the chunked transfer coding grammar.
36
+
The CRLF after chunk data is mandatory per the grammar. However, RFC 9112 §2.2 states:
37
+
38
+
> "Although the line terminator for the start-line and fields is the sequence CRLF, a recipient MAY recognize a single LF as a line terminator and ignore any preceding CR."
39
+
40
+
This means a server MAY accept bare LF as a line terminator — both strict rejection and lenient acceptance are RFC-compliant.
41
+
42
+
**Pass:** Server rejects with `400` (strict, safe).
43
+
**Warn:** Server accepts and responds `2xx` (RFC-valid per §2.2 MAY accept bare LF).
38
44
39
45
## Why it matters
40
46
41
47
If one parser accepts bare LF as a chunk data terminator and another requires strict CRLF, they disagree on where the chunk data ends. The byte that the strict parser considers part of chunk data is treated as the next chunk-size line by the lenient parser — a classic desynchronization vector.
@@ -32,15 +31,22 @@ The final trailer terminator uses bare LF (`\n`) instead of CRLF (`\r\n`).
32
31
33
32
## What the RFC says
34
33
35
-
> "The trailer section is terminated by an empty line (CRLF)." — RFC 9112 Section 7.1
34
+
> "The trailer section is terminated by an empty line (CRLF)." — RFC 9112 §7.1
36
35
37
-
The chunked message ends with `last-chunk CRLF trailer-section CRLF`. Both CRLF sequences are mandatory.
36
+
The chunked message ends with `last-chunk CRLF trailer-section CRLF`. Both CRLF sequences are mandatory per the grammar. However, RFC 9112 §2.2 states:
37
+
38
+
> "Although the line terminator for the start-line and fields is the sequence CRLF, a recipient MAY recognize a single LF as a line terminator and ignore any preceding CR."
39
+
40
+
This means a server MAY accept bare LF — both strict rejection and lenient acceptance are RFC-compliant.
41
+
42
+
**Pass:** Server rejects with `400` (strict, safe).
43
+
**Warn:** Server accepts and responds `2xx` (RFC-valid per §2.2 MAY accept bare LF).
38
44
39
45
## Why it matters
40
46
41
47
If a front-end parser accepts bare LF as the end of the chunked body but a back-end requires strict CRLF, the back-end may continue waiting for data or interpret subsequent bytes differently. This desync between message boundary detection is a smuggling vector.
Content-Length with leading zeros: `Content-Length: 007`.
16
+
Content-Length with leading zeros: `Content-Length: 005`.
17
17
18
18
```http
19
19
POST / HTTP/1.1\r\n
@@ -26,8 +26,15 @@ hello
26
26
27
27
## What the RFC says
28
28
29
-
While `007` matches `1*DIGIT`, leading zeros create ambiguity. Some parsers may interpret as octal, some as decimal.
29
+
The `Content-Length` grammar is `1*DIGIT`. Since `005` matches `1*DIGIT`, it is technically valid. However, leading zeros create ambiguity — some parsers may interpret the value as octal (base-8), while others treat it as decimal.
30
+
31
+
## Why it matters
32
+
33
+
This is a **security vs. strict RFC compliance** tension. The value `005` is grammatically valid, so a server that accepts it and parses it as decimal 5 is not violating the RFC. However, if a front-end and back-end disagree on whether `005` means 5 (decimal) or 5 (octal), they agree by coincidence. For values like `010` (decimal 10 vs. octal 8), disagreement causes body boundary misalignment — a smuggling vector.
34
+
35
+
**Pass:** Server rejects with `400` (strict, safe).
36
+
**Warn:** Server accepts and responds `2xx` (RFC-valid but potentially risky in proxy chains).
> "If a message is received with both a Transfer-Encoding and a Content-Length header field, the Transfer-Encoding overrides the Content-Length. Such a message might indicate an attempt to perform request smuggling or response splitting and **ought to** be handled as an error."
33
33
34
-
The "ought to" language is between SHOULD and MAY.
34
+
The "ought to" language is between SHOULD and MAY. RFC 9112 §6.3 further clarifies:
35
+
36
+
> "If a Transfer-Encoding header field is present and the chunked transfer coding is the final encoding, the message body length is determined by reading and decoding the chunked data..."
37
+
38
+
This means a server **MAY** reject the message or process it using Transfer-Encoding alone — both are RFC-compliant.
39
+
40
+
**Pass:** Server rejects with `400` (strict, safe).
41
+
**Warn:** Server accepts and responds `2xx` (RFC-valid, using TE to determine body length).
35
42
36
43
## Why it matters
37
44
38
45
This is **the** classic request smuggling setup. If the front-end uses Content-Length and the back-end uses Transfer-Encoding (or vice versa), they disagree on body boundaries.
`Transfer-Encoding: , chunked` — leading comma before `chunked`.
18
17
19
18
```http
20
19
POST / HTTP/1.1\r\n
@@ -30,12 +29,18 @@ The Transfer-Encoding value starts with a leading comma before `chunked`.
30
29
31
30
## What the RFC says
32
31
33
-
> The list syntax does not allow empty list members (leading comma).
32
+
> "A recipient MUST parse and ignore a reasonable number of empty list elements." — RFC 9110 §5.6.1
33
+
34
+
The leading comma produces an empty list element before `chunked`. Since RFC 9110 §5.6.1 requires recipients to ignore empty list elements, a server that strips the empty element and processes `chunked` normally is RFC-compliant.
35
+
36
+
**Pass:** Server rejects with `400` (strict, safe).
37
+
**Warn:** Server accepts and responds `2xx` (RFC-valid per §5.6.1 empty-element handling).
34
38
35
39
## Why it matters
36
40
37
-
Some parsers strip leading commas and see "chunked", while others reject the value. This discrepancy enables smuggling.
41
+
Some parsers strip leading commas and see "chunked", while others reject the value entirely. This discrepancy enables smuggling when front-end and back-end parsers disagree on whether Transfer-Encoding is valid.
> "If any transfer coding other than chunked is applied to a request payload body, the sender MUST apply chunked as the final transfer coding." — RFC 9112 §7
31
+
> "If a Transfer-Encoding header field is present in a request and the chunked transfer coding is not the final encoding, the message body length cannot be determined reliably; the server MUST respond with the 400 (Bad Request) status code and then close the connection." — RFC 9112 §6.3
32
+
33
+
This is MUST-level language — servers have no discretion here.
32
34
33
35
## Why it matters
34
36
35
-
If chunked isn't final, the server cannot determine body boundaries. This can be exploited for smuggling.
37
+
If chunked isn't the final encoding, the server cannot determine body boundaries. This can be exploited for smuggling.
0 commit comments