### Overview
This page contains request classification tiers and reasons as well as mitigations, with explanations for some non-trivial cases.
### Request classification
`http_desync_guardian` is a library for analyzing and classifying HTTP/1.x requests to provide customers security balanced with necessity to serve traffic for legacy or proprietary systems (not always RFC compliant).
* `Compliant` - RFC compliant requests (*)
* `Acceptable` - non RFC compliant requests, but which do not represent security risks
* `Ambiguous` - requests that might be treated differently by different HTTP servers and therefore may lead to HTTP Desync issues (and request splitting/smuggling as a possible consequence)
* `Severe` - either malformed or highly likely crafted to trick HTTP parsers and cause HTTP de-synchronization.
### Recommended `http_desync_guardian` Modes
| Compliant | Allowed | Allowed |
| Acceptable | Allowed | Blocked |
| Ambiguous | Allowed¹ | Blocked |
| Severe | Blocked | Blocked |
¹ Route the requests but closes the client and target connections.
For `Blocked` requests the client connection must be closed.
If you are concerned about potential impact, _Monitoring mode_ offers a metrics-only approach to assess prior to switching.
### Classification Reasons
* `Compliant`
* `Compliant` - a compliant request
* `Acceptable`
* `NonCompliantHeader` - non-essential header containing a non-ASCII or control characters (CTL) - i.e. special invisible characters.
* `SpaceInUri` - unescaped space in the URI
* `NonCompliantVersion` - version which contains extra spaces, missing (i.e. HTTP/0.9) or matches HTTP/1.[2-9]
* `GetHeadZeroContentLength` - GET/HEAD request with a “Content-Length: 0” header
* `Ambiguous`
* `EmptyHeader` - if there is an empty header or a line with whitespaces only in the request
* `AmbiguousUri` - an URI containing CTL characters
* `UndefinedContentLengthSemantics` - Content-Length for GET/HEAD requests
* `UndefinedTransferEncodingSemantics` - Transfer-Encoding for GET/HEAD requests
* `DuplicateContentLength` - duplicated Content-Length header (same value)
* `BothTeClPresent` - both Transfer-Encoding and Content-Length are present in the request
* `SuspiciousHeader` - a header that can be normalized to `Transfer-Encoding` or `Content-Length` using common text normalization techniques (sanitation, case normalization, delimiters normalization).
* `Severe`
* `BadHeader` - header containing null-character or CR
* `BadUri` - URI containing null-character or CR
* `BadVersion` - malformed version
* `MultipleContentLength` - different Content-Length headers
* `BadContentLength` - a non-parseable value or an invalid number
* `MultipleTransferEncodingChunked` - multiple Transfer-Encoding: chunked headers
* `BadTransferEncoding` - unknown Transfer-Encoding value
* `BadMethod` - malformed method
* `Parsing` raw-requests
* `NonCrLfLineTermination` (Acceptable) - allowing “\n” line termination (similar to Nginx).
* `MultilineHeader` (Ambiguous) - multi-line headers are non RFC compliant (except Content-Type)
* `PartialHeaderLine` (Ambiguous) - if a header line was not terminated
* `MissingLastEmptyLine` (Ambiguous) - there is no empty line at the end of request
* `MissingHeaderColon` (Ambiguous) - header line doesn’t have colon separator
* `MissingUri` (Ambiguous) - there is no URI in the request line
### Details on certain classifications
#### Undefined Content-Length/Transfer-Encoding Semantics (UndefinedTransferEncodingSemantics, UndefinedContentLengthSemantics)
A payload within a GET/HEAD request message has no defined semantics.
https://tools.ietf.org/html/rfc7231#section-4.3
https://medium.com/@knownsec404team/protocol-layer-attack-http-request-smuggling-cc654535b6f
3.1 GET Request with CL != 0
https://portswigger.net/web-security/request-smuggling/exploiting
See "Capturing other users’ requests"
https://www.cgisecurity.com/lib/HTTP-Request-Smuggling.pdf
see "EXAMPLE #3"
#### Both Content-Length and Transfer-Encoding are present (BothTeClPresent)
If a request containing both Content-Length and Transfer-Encoding was received, it means that the sender didn’t follow RFC, and thus there is a chance that request boundaries might be out of sync with the sender.
> 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 (Section 9.5) or response splitting
(Section 9.4) and ought to be handled as an error. A sender MUST
remove the received Content-Length field prior to forwarding such
a message downstream.
https://tools.ietf.org/html/rfc7230#section-3.3.2
#### Multi-line headers (MultilineHeader)
Multi-line headers have been deprecated in RFC 7230, and different engines may either support it or not, which provides malicious actors a toolkit to trick parser to “see” headers that are not there or vice versa. That’s why we mark requests containing multi-line headers as Ambiguous (except the Content-Type header).
> Historically, HTTP header field values could be extended over multiple lines by
preceding each extra line with at least one space or horizontal tab (obs-fold).
This specification deprecates such line folding except within the message/http media
type (Section 8.3.1). A sender MUST NOT generate a message that includes line folding
(i.e., that has any field-value that contains a match to the obs-fold rule) unless the message
is intended for packaging within the message/http media type.
https://tools.ietf.org/html/rfc7230#section-3.2.4
#### Multiple Transfer-Encoding Chunked (MultipleTransferEncodingChunked)
> A sender MUST NOT apply chunked more than once to a message body
https://tools.ietf.org/html/rfc7230#section-3.3.1
#### Multiple Content-Length Headers (MultipleContentLength, DuplicateContentLength)
If there are multiple different Content-Length headers (different values) the request is marked as Severe.
In the case of multiple but same values, it falls into DuplicateContentLength category (marked as Ambiguous).
> If a message is received that has multiple Content-Length header
fields with field-values consisting of the same decimal value, or a
single Content-Length header field with a field value containing a
list of identical decimal values (e.g., "Content-Length: 42, 42"),
indicating that duplicate Content-Length header fields have been
generated or combined by an upstream message processor, then the
recipient MUST either reject the message as invalid or replace the
duplicated field-values with a single valid Content-Length field
containing that decimal value prior to determining the message body
length or forwarding the message.
https://tools.ietf.org/html/rfc7230#section-3.3.2
#### Suspicious headers (SuspiciousHeader)
There is a range of attacks to masquerade Transfer-Encoding and Content-Length headers, so some engines in the chain will see them while others won’t. For example:
```
Transfer-Encoding : chunked
Content-Length: 100
```
In this case, some engines may reject this request, as it’s not RFC compliant. Some may sanitize the space before the colon and treat as it has “Transfer-Encoding: chunked” while some may see “Transfer-Encoding[space]” and ignore it. It is the simplest case to illustrate this idea, but there are many others. For instance:
```
Transfer-Encodıng: chunked
```
Small Dotless I becomes ASCII “I” on upper-case transformation which may trick some engines. Or have a CTL character:
```
Transfer_Encoding: chunked
\x01Transfer-Encoding: chunked
Transfer-Encoding\b: chunked
```
Some engines may normalize delimiters or non-letters, e.g., using regular expressions, etc. (especially using standard string trimming routines that may have different behaviors in different platforms).
To mitigate these risks, we determine the similarity of headers to `Transfer-Encoding` and Content-Length and mark requests as Ambiguous if any of these deviations are detected.
### Mitigations
There are two types of mitigations:
* Reject request with 400 and close the connection
* Serve the request but disable connection re-use on both front-end and back-end.
Why connection is closed after a `Severe` request
In this case, we cannot establish request boundaries and tell when the next request starts.
Why connections are both FE/BE connections closed after an Ambiguous request?
Let’s start with not re-using a backend connection example:

1. Attacker sends a request, such as Proxy only sees POST /foo while backend also sees GET /poison
2. However Proxy marks the request as Ambiguous
3. Proxy closes the connection after the response
4. The /poison response is dropped as the connection is not going to be re-used.
This seems to be efficient, but falls short if there is a layer in front of the proxy:

1. In this case let’s assume Desync happens between CDN and the Proxy and Proxy marks the request as Ambiguous
2. While Proxy closes the BE connection, it’s not helpful
3. The /poison response is still served via re-used front-end connection
But if both FE/BE connections are closed, then HTTP Desync is prevented:

1. Same as in the previous example, let’s assume Desync happens between CDN and the Proxy and Proxy marks the request as Ambiguous
2. Now Proxy closes both FE/BE connections.
3. The /poison response is dropped.