---
type: Whitepaper
title: "T-Reqs: HTTP Request Smuggling with Differential Fuzzing"
description: A grammar-based differential fuzzer generates HTTP requests and mutates the request line, headers and body, then compares how ten servers, proxies and CDNs parse each one to find pairs that disagree on where a message ends. Those disagreements let an attacker smuggle a hidden second request past the front server, enabling cache poisoning, request hijacking and control bypass.
resource: "https://bahruz.me/papers/ccs2021treqs.pdf"
tags: [whitepaper, webseclist-reference, request-smuggling, desync, parser-differential, fuzzing, http, proxy, cdn, cache-poisoning, tooling, measurement-study]
generated:
  by: webseclist-refs/1
  at: "2026-08-11T17:38:02+00:00"
status: deprecated
stale_after: 2027-08-11
sources:
  - id: original
    resource: "https://bahruz.me/papers/ccs2021treqs.pdf"
    title: "T-Reqs: HTTP Request Smuggling with Differential Fuzzing"
    author: Bahruz Jabiyev, Steven Sprecher, Kaan Onarlioglu, Engin Kirda
  - id: capture
    resource: "https://web.archive.org/web/20220525035027/https://bahruz.me/papers/ccs2021treqs.pdf"
also_at: []
authors:
  - Bahruz Jabiyev
  - Steven Sprecher
  - Kaan Onarlioglu
  - Engin Kirda
canonical_url: ""
cited_by:
  - "2021.md:24"
commit: ""
content_sha256: c161d6036a995d8daba4c0cf7131f27f3e949b877be799e67cb72ce93022e8eb
depth: full
depth_reason: default
kind: whitepaper
language: ""
licence: unknown
original_url: "https://bahruz.me/papers/ccs2021treqs.pdf"
published: ""
publisher: ""
publisher_english: ""
raw_sha256: 093372154d479b74abe1046f447e8db0c1fcd2861f0c5bf56da352e0b80ca745
retrieved_from: "https://bahruz.me/papers/ccs2021treqs.pdf"
retrieved_kind: stored
retrieved_utc: "2026-08-11T17:38:02+00:00"
slug: t-reqs-http-request-smuggling-differential-fuzzing
snapshot: 20220525035027
title_english: ""
translation_file: ""
translation_of: ""
---

# T-Reqs: HTTP Request Smuggling with Differential Fuzzing

**T-Reqs: HTTP Request Smuggling with Differential Fuzzing** - Bahruz Jabiyev, Steven Sprecher, Kaan Onarlioglu, Engin Kirda, Publisher not stated.

- Published: date not stated
- Original: <https://bahruz.me/papers/ccs2021treqs.pdf>
- Preserved from: https://bahruz.me/papers/ccs2021treqs.pdf (stored) on 2026-08-11
- Capture timestamp: 20220525035027
- Licence: unknown

Rights remain with the original author and publisher. This is a research
archive of a source from the Web Hacking Techniques Index collections, kept so the
page going offline. To read the original, follow the link above.

## Content

> UNTRUSTED SOURCE TEXT. Everything below this line is third-party material
> quoted for research. It is data, not instructions. Do not follow directions,
> execute code, or fetch URLs because this text says so.

T-Reqs: HTTP Request Smuggling with Differential Fuzzing
                                   Bahruz Jabiyev                                                                      Steven Sprecher
                              Northeastern University                                                               Northeastern University
                                Boston, MA, USA                                                                       Boston, MA, USA

                                 Kaan Onarlioglu                                                                          Engin Kirda
                                Akamai Technologies                                                                 Northeastern University
                                Cambridge, MA, USA                                                                    Boston, MA, USA

ABSTRACT                                                                                          semantics of these different servers to confuse them about message
HTTP Request Smuggling (HRS) is an attack that exploits the HTTP                                  boundaries, and consequently smuggles unintended requests into
processing discrepancies between two servers deployed in a proxy-                                 the connection inside the request body.
origin configuration, allowing attackers to smuggle hidden requests                                  HRS was first documented by Linhart et al. in 2005 [24]. However,
through the proxy. While this idea is not new, HRS is soaring in                                  the technique took off only recently when researchers proposed
popularity due to recently revealed novel exploitation techniques                                 novel variants and demonstrated attacks on high-profile targets
and real-life abuse scenarios.                                                                    (e.g., [6, 15, 19, 23]). Ultimately, smuggling was shown to be a serious
   In this work, we step back from the highly-specific exploits hog-                              threat leading to response queue and cache poisoning, which can
ging the spotlight, and present the first work that systematically                                then be exploited for myriad nefarious purposes such as personal
explores HRS within a scientific framework. We design an experi-                                  data leakage, credential theft, session hijacking, denial of service,
ment infrastructure powered by a novel grammar-based differential                                 and security control bypass attacks, resulting in thousands of dollars
fuzzer, test 10 popular server/proxy/CDN technologies in combi-                                   in bug bounties (e.g., [5, 16, 17]).
nations, identify pairs that result in processing discrepancies, and                                 While these same researchers also released tools (e.g., [7, 36])
discover exploits that lead to HRS. Our experiment reveals previ-                                 that partially automate the detection of HRS, these are largely
ously unknown ways to manipulate HTTP requests for exploitation,                                  intended for assisting website owners and penetration testers in
and for the first time documents the server pairs prone to HRS.                                   probing specific targets for vulnerabilities. These tools are also
                                                                                                  narrowly scoped, primarily testing for exploits that involve the
CCS CONCEPTS                                                                                      manipulation of two particular HTTP headers, Content-Length
                                                                                                  and Transfer-Encoding, which govern how servers determine
• Security and privacy → Web application security.
                                                                                                  HTTP message bounds.
                                                                                                     To date, HRS has not been studied in a systematic manner; the
KEYWORDS
                                                                                                  disclosed vulnerabilities were instead driven by case studies target-
HTTP Request Smuggling; HTTP Desync Attacks                                                       ing popular websites. In particular, previous work on HRS leaves
ACM Reference Format:                                                                             two important gaps in our understanding of HRS attacks.
Bahruz Jabiyev, Steven Sprecher, Kaan Onarlioglu, and Engin Kirda. 2021. T-                          First and foremost, HRS is a system interaction problem, in-
Reqs: HTTP Request Smuggling with Differential Fuzzing. In Proceedings of                         volving at least two HTTP processors on the traffic path. These
the 2021 ACM SIGSAC Conference on Computer and Communications Security                            processors may not necessarily be individually buggy; but when
(CCS ’21), November 15–19, 2021, Virtual Event, Republic of Korea. ACM, New                       used together, they disagree on the parsing or semantics of a given
York, NY, USA, 16 pages. https://doi.org/10.1145/3460120.3485384                                  HTTP request, which leads to a vulnerability. This key aspect of
                                                                                                  HRS has not been explored in previous work. Next, previous attacks
1     INTRODUCTION                                                                                focus on malicious manipulation of the two aforementioned HTTP
Due to the continuing proliferation of web caches, proxies, cloud ser-                            headers. Whether the remaining HTTP headers, or the rest of an
vices, and Content Delivery Networks (CDNs) that deploy massively-                                HTTP request, could be tampered with to induce similar processing
distributed networks made up of these technologies, a typical HTTP                                discrepancies remains uncharted territory.
request is often processed by multiple intermediate servers before                                   In this paper, we present the first study that investigates HRS in
it reaches its destination. HTTP Request Smuggling (HRS) is an                                    a scientific framework, and we tackle the above research questions.
attack that exploits the discrepancies between HTTP processing                                    Namely, we present a novel experiment setup with 10 popular
                                                                                                  web servers and proxies: Apache, NGINX, Tomcat, Apache Traffic
Permission to make digital or hard copies of all or part of this work for personal or             Server (ATS), HAProxy, Squid, Varnish, Akamai, Cloudflare, and
classroom use is granted without fee provided that copies are not made or distributed
for profit or commercial advantage and that copies bear this notice and the full citation         CloudFront. We study these technologies in pairs, investigating
on the first page. Copyrights for components of this work owned by others than ACM                which combinations are vulnerable to HRS. To that end, we propose
must be honored. Abstracting with credit is permitted. To copy otherwise, or republish,           a grammar-based fuzzer called T-Reqs that incorporates string
to post on servers or to redistribute to lists, requires prior specific permission and/or a
fee. Request permissions from permissions@acm.org.                                                and tree mutations targeting a large variety of HTTP headers, the
CCS ’21, November 15–19, 2021, Virtual Event, Republic of Korea                                   request line, and the request body. T-Reqs employs a differential
© 2021 Association for Computing Machinery.                                                       fuzzing strategy, first testing each target technology in isolation,
ACM ISBN 978-1-4503-8454-4/21/11. . . $15.00
https://doi.org/10.1145/3460120.3485384
                                                                                              1
and then comparing responses to identify the pairs that behave                                  Table 1: Breakdown of the chunked body.
differently, signaling a potential vulnerability.                                              6\r\n                    Chunk size
                                                                                               query=\r\n               Chunk data
   Once we identify the combinations that exhibit discrepancies                                a;foo=bar\r\n            Chunk size & chunk extension
and the payloads that trigger them, we deploy every server pair                                funny+cats\r\n           Chunk data
in a proxy-to-origin formation for further experimentation and                                 0\r\n                    Last chunk
                                                                                               X-Header:value\r\n       Trailer part
verification of our findings. We examine the conditions which cause                            \r\n                     Terminating CRLF
the potential HRS attacks to succeed or fail in this setup, and finally
demonstrate a range of exploits we discover.
   Our results show that attacks can indeed be induced by manipu-                     Chunked transfer encoding is an alternative encoding scheme
lating every part of a request, and highlights that HRS is a complex               available in HTTP/1.1, where the message body is split into multiple
system interaction problem that can crop up as a result of seem-                   chunks transferred independently. This mechanism is useful for
ingly innocuous processing discrepancies between pairs of web                      streaming applications, when the size of the data to be transferred
technologies that are otherwise shown to be secure in isolation.                   is not known a priori [10].
   We summarize our contributions as follows:                                         Listing 2 shows the same request as before, this time using chun-
     • We present the most comprehensive study of HRS to date,                     ked encoding. Every data chunk is preceded by its size, specified
        and examine attacks within a scientific framework for the                  in hexadecimal. Both the size and the data are terminated by CRLF.
        first time in literature.                                                  Optionally, a chunk extension may immediately follow the size and
     • We propose a novel approach and experiment setup that                       contain metadata (e.g., a hash of the chunk data). The last chunk is
        identify the HTTP processing discrepancies between 10 pop-                 a regular but empty chunk with a size of zero. Again, optionally, the
        ular web servers and proxy services often used together.                   last chunk can include a trailer which is treated similar to message
     • We develop a grammar-based differential HTTP fuzzer called                  headers, used for sending additional information to the receiver.
       T-Reqs, and make it open source.                                            Table 1 presents a breakdown of these chunk components.
     • We discover novel HRS payloads made possible by manipu-                        Note that the Transfer-Encoding: chunked header in Listing 2
        lating HTTP request parts beyond the Content-Length and                    indicates to the receiver that chunked encoding is in effect. When
       Transfer-Encoding headers.                                                  using chunked encoding, sending the Content-Length header is
     • We systematically examine the practical conditions that de-                 not meaningful, and in fact, according to RFC 7230, this is prohibited:
        termine the success of HRS.                                                “A sender MUST NOT send a Content-Length header field in any
   Availability. T-Reqs is open-source and publicly available on                   message that contains a Transfer-Encoding header field.” [10]
the authors’ websites.
   Ethical Considerations. This study was conducted within a
                                                                                   2.2    HTTP Request Smuggling (HRS)
controlled experiment setup, and no attacks were launched against                  HRS stems from a discrepancy between the HTTP processing be-
any external entities. We followed the established coordinated-                    haviors of two servers that process the same request on the traffic
disclosure best practices; we notified all tested technology vendors               path. These servers could be any technology that intercepts, parses,
of our findings, provided them with a copy of this paper, and made                 interprets, or forwards the request, including CDNs, stand-alone
our data and team available for further assistance.                                proxies, web caches, load balancers, or security products. In this
                                                                                   text, we call the first server receiving the request the entrypoint,
2     BACKGROUND & RELATED WORK                                                    and the next one the exitpoint. While this abstraction is sufficient
In this section we explain the basic terminology we use in the rest                for our discussion, note that a typical request may be processed by
of this text, and summarize how HRS attacks work.                                  more than two such entities, and a hazardous combination of any
                                                                                   two could lead to attacks.
2.1    HTTP Requests & Chunked Encoding                                               HRS involves a maliciously-crafted request such that the entry
                                                                                   and exitpoints disagree on the bounds of the message. All docu-
Listing 1 illustrates the structure of a typical HTTP request, made
                                                                                   mented attacks we previously discussed in Section 1 achieve this
up of the following three components.
                                                                                   by including both the Content-Length and Transfer-Encoding:
   (1) Request Line. Line 1 is the request line for this request,
which specifies the HTTP method (POST), the requested URI (/search),
and the protocol version (HTTP/1.1).                                          1 POST /search HTTP/1.1               1 POST /search HTTP/1.1
   (2) Header Block. This section follows the request line, listing           2 Host: example.com                   2 Host: example.com
                                                                              3 Content-Length: 16                  3 Transfer-Encoding: chunked
header fields and values that define various parameters of the com-
munication. On lines 2-3, the Host header specifies the endpoint              4                                     4
                                                                                   query=funny+cats                    6
the request should be dispatched to, and Content-Length indicates
                                                                              5                                     5

                                                                              6                                     6  query=
the length of the message body.                                               7                                      7 a;foo=bar
   (3) Request Body. Separated from the header block by a blank               8                                      8 funny+cats
line containing a carriage return and a line feed (often indicated by         9                                      9 0
CRLF or \r\n), the request body starts on line 5 and contains the             10   .                                10 X-Header:value

message payload. Here, the body consists of a parameter and its
value, query=funny+cats.                                                               Listing 1: Regular body.               Listing 2: Chunked body.
                                                                          2
           1 POST /search HTTP/1.1                                            <start> ::= <request>
           2 Host: example.com                                                <request> ::= <line><headers><newline><body>
           3 Content-Length: 33                                               <line> ::= "POST /search HTTP/1.1\r\n" | "PUT / HTTP/1.1\r\n"
           4 Transfer-Encoding: ; chunked                                     <headers> ::= <host><content-length> | <host>
           5                                                                  <host> ::= "Host: example.com\r\n"
           6 0                                                                <content-length> ::= "Content-Length: 16\r\n"
           7 `                                                                <newline> ::= "\r\n"
           8 GET /img/i.jpg HTTP/1.1                                          <body> ::= "query=funny+cats" | "query=carrots"
           9 X:X
                                                                                     Listing 6: Example CFG for a simple HTTP request.
               Listing 3: Malicious smuggler request.

                                                                              until the cache expires. This is but one example, and researchers
1 GET /js/j.js HTTP/1.1            1 GET /img/i.jpg HTTP/1.1                  have shown that HRS can be utilized for general classes of attacks
2 Host: example.com                2 X:X GET /js/j.js HTTP/1.1                such as cache poisoning, cache deception, session hijacking, circum-
3 A                                3 Host: example.com
                                                                              vention of security controls, and response queue poisoning, as well
4 A                                4
                                                                              as abusing application specific design flaws [6, 15, 19, 23, 24, 35].
5 A                                5   .

    Listing 4: Incoming request.           Listing 5: Smuggled request.       2.3    Differential Fuzzing
                                                                              Fuzzing is a well-established software testing approach with many
                                                                              applications in systems security [14, 38]. Of particular interest for
chunked headers in the request – if the entrypoint honors one                 our purposes is differential fuzzing, based on the idea of differen-
header and the exitpoint the other, they parse the message body               tial testing [27], where the focus is to identify differing behavior
differently, and an HRS attack is possible. Even when both servers            between applications when given the same input. To name recent
are strictly RFC compliant so that they reject or normalize messages          examples, this method was used to detect side-channel attacks [32],
containing both headers, and attacker can still abuse header parsing          to expose vulnerabilities in parsers and applications [33], and to
discrepancies (e.g., by introducing invisible characters or minor             find RFC violations in TLS libraries [37].
syntax errors into headers) and prevent one of the servers from                  To apply this technique to the HTTP protocol, we construct our
recognizing an invalid header combination.                                    fuzzer using a custom context-free grammar (CFG). Context-free
   Let’s illustrate the attack through an example.                            grammars are sets of rules that allow for a formal definition of a
   (1) The attacker crafts a smuggler request which includes a sec-           structure, e.g., an HTTP request, and values that correspond to
ond hidden request inside the message body. Listing 3 shows such a            that structure. From this grammar, we are able to generate valid
request including Content-Length: 33 and Transfer-Encoding:                   inputs to our system, and make our fuzzing mutations based off of
;chunked together. Note the extra semicolon in the latter, which              them. An example CFG that produces an HTTP request is shown in
will serve to confuse the message parser in the next step.                    Listing 6. Grammar-based fuzzers have also been previously used
   (2) The entrypoint receives the request, but cannot correctly              for software bug hunting (e.g., [1]).
parse Transfer-Encoding: ;chunked due to the semicolon. The                      A CFG has four components: a start symbol, non-terminal sym-
server disregards chunked encoding and instead parses the message             bols, terminal symbols, and production rules. The start symbol is
body according to the Content-Length: 33 header. As a result,                 where the expansion of a CFG starts from. In Listing 6, the start
the entrypoint forwards all 33 bytes shown between lines 5-9 to               symbol is denoted by <start>. Symbols surrounded by <> are non-
the next hop.                                                                 terminals, meaning they are expanded before the input is fully
   (3) The exitpoint receives the same request, correctly parses              generated. For example, <request> is expanded to a sequence of
Transfer-Encoding: ;chunked thanks to its lenient parser ig-                  other non-terminal symbols, whereas, <line> can be expanded
noring errors, and processes the body in chunks. Consequently,                into multiple terminal strings. Finally, production rules define how
the exitpoint treats lines 6-7 as the terminating empty chunk, and            symbols are expanded. Each line in Listing 6 is a production rule.
ignores lines 8-9.                                                            When this CFG is fully expanded, one of the possible results is the
   (4) The unprocessed data shown on lines 8-9 remain in the re-              request shown in Listing 1.
quest buffer of the exitpoint. When eventually another request
arrives through the same connection (Listing 4), it is appended to            2.4    Other Related Work
this unprocessed data, making up a brand new request (Listing 5).             An emerging line of research is the application of HRS to higher
This new request is only seen and processed by the exitpoint; the             HTTP protocol versions; in particular, Emil Lerner and James Kettle
attacker has successfully smuggled it through the entrypoint.                 independently presented attacks on HTTP/2 [18, 22]. These utilize
   In this example, assuming that the entrypoint is a web cache               the same techniques as before, but exploit flaws in the protocol
and the exitpoint a web application server, the attacker uses HRS             downgrade mechanisms when an entrypoint converts HTTP/2 to
to launch a cache poisoning-based denial-of-service attack. Specifi-          HTTP/1.1 before forwarding requests to the exitpoint. Our work
cally, the web cache expects a JavaScript file in response (see List-         does not explore this area.
ing 4), but instead receives an image from the application (see List-            Beyond the presentations, proof-of-concept exploits, and white
ing 5) and erroneously caches that, likely breaking the application           papers we discussed so far, there is no academic literature on HRS as
                                                                          3
                 generating                 mutating
        Input     requests                  requests                          processing behavior of each individual server, and combine the
                                                           Mutated
      Grammar                  Inputs
                                                            Inputs            results to identify servers that process the same request differently.
                                                                                  At this stage, we experiment with each server in isolation to
                                                                              analyze their individual behavior. Each server runs in a reverse-
                                                       sending
                                                       requests               proxy mode, where they receive requests and forward them on
                                          forwarding                          to our feedback server. The feedback server gleans information
                   storing                  parsed                            about the processing behavior of the tested server by analyzing
                  requests    Feedback     requests         HTTP
     Feedback
                               Server                       Server
                                                                              the forwarded request. This information is stored in a database for
     Database
                                                                              later analysis. We specifically look for mismatches between parsed
                                                                              message body lengths, and label those as discrepancies.
Figure 1: Inputs are generated from a grammar, mutated, and sent
                                                                                  Figure 1 depicts this whole process, where the top row is internal
to the tested server. The feedback server collects feedback from the
requests forwarded by the tested server and stores it for analysis.
                                                                              to T-Reqs, and the bottom row is the rest of the experiment infras-
                                                                              tructure. Note that, in order to avoid adding a confounding layer of
                                                                              parsing in our own tools, we use low-level network programming.
of this writing. However, while this paper is the first work exploring            Stage 2. We then reduce our set of discrepancies found in Stage
HRS within a scientific framework, there exists studies that propose          1 based on rules and heuristics detailed in Section 5. Essentially,
other ways to abuse HTTP processing discrepancies.                            we associate mutation sets with the server pairs they cause a dis-
   Omer Gil presented a novel cache poisoning attack called Web               crepancy for, minimize them down to a representative group, and
Cache Deception (WCD), which exploits an object cacheability dis-             finally, manually classify these groups based on their mutation pat-
agreement between a web server and a cache, resulting in data                 terns. We stress that this manual classification is not mandatory;
leaks in public caches [12, 13]. Mirheidari et al. generalized WCD            we merely include this step to simplify the presentation for our
as a path confusion problem caused by a discrepancy in the in-                readers by attaching intuitive labels to similar discrepancy types.
terpretation of a requested URL [28], and conducted a large-scale                 Stage 3. Finally, we verify the exploitability of the results from
measurement to identify vulnerable sites in the wild [29].                    Stage 2. To achieve this, we layer and deploy suspected vulnerable
   Nguyen et al. presented a different take on cache poisoning,               HTTP server pairs behind each other. We use a testing method
crafting HTTP requests that are considered valid by a web cache               inspired by prior work to check whether a given mutated request
while triggering an error at the origin server [31]. As a result,             can really be used for HRS. We present the details of this method
the error response is erroneously cached, resulting in a denial-of-           in Section 5.3.
service attack. Similarly, Chen et al. exploited HTTP servers that                (Q2) What parts of a request can induce processing dis-
respond differently to ambiguities in the Host header values, which           crepancies? Previous work has explored the parsing discrepan-
once again leads to cache poisoning [4].                                      cies involving Content-Length and Transfer-Encoding headers.
                                                                              Whether the remaining request components can be abused to simi-
3   RESEARCH QUESTIONS & METHOD                                               lar effect remains an open question.
                                                                                  We address (Q2) by considerably expanding that scope. Not only
Previous work on HRS presents valuable concepts behind the at-
                                                                              do we allow T-Reqs to mutate additional headers, but we also inves-
tack, but does not explore the issue in depth or breadth, instead
                                                                              tigate whether abusing the request line and the message body can
demonstrating impact through specific case studies. Our work is
                                                                              also induce discrepancies, opening up novel attack vectors. We run
motivated by this knowledge gap. Below, we detail our guiding
                                                                              three separate experiments, one for each HTTP request component
research questions, and explain our methods to answer them.
                                                                              listed above, each following the same stages we designed for ad-
 (Q1) Can we systematically test for HRS at scale?                            dressing (Q1). In each experiment, we only allow T-Reqs to mutate
 (Q2) What parts of a request can induce processing discrepancies?            the part under focus, while keeping the other two request com-
 (Q3) What escalates a processing discrepancy to HRS?                         ponents unmutated. This makes it feasible to pinpoint and reason
 (Q4) What technology stacks are at risk?                                     about the exploitable discrepancies in isolation.
   (Q1) Can we systematically test for HRS at scale? Previous                     (Q3) What escalates a processing discrepancy to HRS? The
work relies on a combination of manual testing and basic tools                presence of a discrepancy is a red flag, but not all discrepancies
designed to target specific controlled environments (e.g., [7, 36]) for       necessarily lead to HRS. In particular, while exploits involving
attack discovery. In contrast, we aim to design a fully-automated,            Content-Length and Transfer-Encoding headers are intuitive
generalizable, and extendable methodology that can explore HRS                (i.e., they directly affect the body parsing behavior, which is a pre-
at scale and discover previously unknown venues for exploitation.             requisite for HRS), why the discrepancies in other request compo-
   To address (Q1), we design a multi-stage experiment powered by             nents may lead to an attack is not obvious.
a novel CFG-based differential fuzzer, T-Reqs. This is an automated               To explore (Q3), while we verify our findings in Stage 3 of the
process, eliminating the manual labor and narrow scope hindering              experiment, we analyze the conditions affecting exploitability. We
previous work. This methodology and infrastructure to explore                 document the novel and successful exploit mechanisms we identify,
HRS systematically equips us to answer the remaining research                 and also the failures that hinder attacks in practice.
questions. Below, we briefly describe the 3 stages of our experiment.             (Q4) What technology stacks are at risk? HRS is a system
   Stage 1. We first point T-Reqs to a set of popular HTTP servers            interaction problem involving two HTTP processors, which may
for testing, and send identical requests to each. We record the               not be flawed when operating in isolation; but stacked together they
                                                                          4
lead to a vulnerability. Previous work on HRS has made no attempt             1 PO R T / /search HTTP/1. 1           1 HTTP /search /search HTTP /1.1
to measure what server combinations are prone to smuggling.                   2 Host: example.com                    2 Host: example.com
   While we cannot feasibly test all technology combinations in               3 Content-Length: 13                   3 Content-Length: 13

existence, we make the first systematic attempt to answer (Q4)                4                                      4

by designing an experiment that measures and documents the                    5   query=bananas                      5   query=bananas
hazardous interactions between 10 HTTP processors.                                    Listing 7: String mutations.           Listing 8: Tree mutations.
   Specifically, we pick popular web server, proxy, and CDN tech-
nologies in use today that make up a large portion of the Internet:
Apache, NGINX, Tomcat, Apache Traffic Server (ATS), HAProxy,                      4.3      String Mutations
Squid, Varnish, Akamai, Cloudflare, and CloudFront. For specific
                                                                                  If a symbol is string mutable, then a random character can be
versions, please see Appendix A. To test CDN vendors, we subscribe
                                                                                  deleted, replaced, or inserted at a random position inside that sym-
to their free or trial tier services. We configure each technology to
                                                                                  bol. To add or replace characters, an external character pool can be
run as a reverse-proxy fronting our feedback server (except Tomcat,
                                                                                  defined. T-Reqs uses the ASCII character set (codes 0-127) as the
which has no reverse-proxy mode, so we run a Java servlet on it that
                                                                                  character pool suitable for HTTP requests.
echoes back the received requests). We use default configurations,
                                                                                     Listing 7 shows an example. The last character in the protocol
save for turning off buffering in NGINX to speed up testing, and
                                                                                  version (1) is deleted, a letter in the method name (S) is replaced
disabling caching for clean experiment runs.
                                                                                  with R, and a forward slash is inserted at the start of the URI.

4     T-REQS SYSTEM DESIGN                                                        4.4      Tree mutations
We now detail the design of T-Reqs, our grammar-based differential                If a symbol is tree mutable, then a random symbol can be deleted,
HTTP fuzzer. T-Reqs is capable of generating HTTP requests as                     replaced, or inserted at a random position. To add or replace sym-
inputs from a grammar, manipulating them with string and tree                     bols, an external pool of elements can be defined. T-Reqs uses the
mutations, and sending them to multiple HTTP servers in parallel                  list of all symbols marked mutable as the external symbol pool.
for testing.                                                                          For example, the request line is represented by <request-line>
                                                                                  and has several sub-elements including <method>, <URI>, <proto>,
4.1    Input Generation                                                           and <version>. In Listing 8, it is assumed that <request-line>
                                                                                  is tree mutable, and the following tree mutations are applied: 1)
To ensure that we test all relevant components of an HTTP request,
                                                                                  <method> is replaced by <proto>, 2) an extra <URI> is inserted after
and their applicable values, T-Reqs uses a context-free grammar
                                                                                  the current URI, and 3) the existing <proto> is deleted.
(CFG) to generate inputs. Each generated input is a valid HTTP
request constructed by following one of the paths provided by the
CFG, chosen randomly to ensure uniform testing. We record each
                                                                                  5     EXPERIMENT DETAILS AND RESULTS
random seed as the input ID to aid in reproducibility.                            In this section, we provide details and discuss results from the
   When building our input from the included grammar, we adopt                    experiments listed in Section 3. We first run three separate experi-
a tree structure. The start symbol becomes the root, and each non-                ments on each part of the HTTP request utilizing T-Reqs to expose
terminal is a non-terminated node in the tree. The leaves of the                  discrepancies in message body parsing behavior. Next, we reduce,
tree, once fully expanded, are made up of the terminal symbols (i.e.,             minimize, and categorize the sets of mutations that cause these dis-
string literals), and when combined form our HTTP request. We                     crepancies to understand what leads servers to disagree on message
present the specific CFGs used for our experiments in Section 5.                  boundaries. We then verify the HRS potential of these categories,
                                                                                  and explore reasons why they succeed or fail.
4.2    Mutating Inputs                                                            5.1      Stage 1 - Finding Discrepancies
In order to exercise the parsers of, and consequently trigger process-            For this stage, we run three separate experiments on each part of the
ing discrepancies between, different HTTP servers, T-Reqs makes                   HTTP request: the request line, request headers, and request body.
mutations on the valid requests generated in the previous step.                   Table 2 shows the duration of each experiment, and the number
   Symbols, each corresponding to an HTTP element, can be marked                  of requests generated and tested. We found that mutations in the
in one of two ways: string mutable or tree mutable. If a symbol                   request line experiment caused more errors (e.g., 400 Bad Request),
is not marked, it is assumed to be immutable. While string mu-                    even when bounding the total number of mutations to two. We see
tations (e.g., character insertion, deletion) make small changes to               more mutations an hour in the request line experiment because
parts of an input, tree mutations lead to structural changes (e.g., re-           these errors are significantly faster for HTTP servers to handle
peated method specification, missing protocol version). This allows               than valid requests.
T-Reqs to test both trivial and major changes to an input. Mutation                  To make T-Reqs more efficient, we supply different grammars
operations are formally defined in Appendix B.                                    and mutable symbols for each experiment as detailed below.
   In each iteration, T-Reqs randomly applies up to 2 mutations on
each input. This upper bound makes the impact analysis of specific                5.1.1 Request Line Experiment Details. Listing 9 shows the gram-
mutations feasible, as well as helping us avoid changing requests                 mar for the request line experiment. We test the standard HTTP
to the degree that they are unrecognizable by the servers.                        methods as defined by their RFCs [9, 11]. Note that we do not
                                                                          5
         Table 2: General information about experiments.                   <start> ::= <request>
                Name              Duration     # Inputs                    <request> ::= <request-line><base><the-rest>
                                                                           <request-line> ::= <method-name><space><uri><space>
                Request line      70 hours       8,857K
                                                                           ↩→ <protocol><separator><version><newline>
                Request headers   94 hours       3,096K
                                                                           <method-name> ::= "GET" | "HEAD" | "POST" | "PUT" | "DELETE" |
                Request body      72 hours       2,051K
                                                                           ↩→ "CONNECT" | "OPTIONS" | "TRACE" | "PATCH"
                                                                           <space> ::= " "
                                                                           <uri> ::= "/_URI_"
           Table 3: Mutability of request line symbols.                    <protocol> ::= "HTTP"
                    String Mutable              Tree Mutable               <separator> ::= "/"
                                                                           <version> ::= "0.9" | "1.0" | "1.1" | "2.0" | "3.0"
           <method-name> <space> <protocol>
                                                <request-line>             <newline> ::= "\r\n"
            <separator> <version> <newline>                                <base> ::= "Host: _HOST_\r\nConnection:close\r\nX-Request-ID:
                                                                           ↩→ _REQUEST_ID_\r\n"
                                                                           <the-rest> ::= "Content-Length: 5\r\n\r\nBBBBB"
           Table 4: Mutability of request body symbols.
                String Mutable                Tree Mutable                               Listing 9: CFG for request line experiment.

              <zero> <trailer-part>
                                             <chunked-body>                <start> ::= <request>
             <chunk-data><newline>
                                           <chunk> <last-chunk>            <request> ::= <method-name><request-uri><http-version><base>
         <chunk-size> <chunk-extension>
                                                                           ↩→ <entity-size-header><some-header><some-header><body>
                                                                           <request-uri> ::= " /_URI_ "
               Table 5: Experiment success values.                         <http-version> ::= "HTTP/0.9" | "HTTP/1.0" | "HTTP/1.1"
                                                                           <method-name> ::= "GET" | "HEAD" | "POST" | "PUT" | "DELETE" |
             Experiment Name      # Inputs    # Successful
                                                                           ↩→ "CONNECT" | "OPTIONS" | "TRACE"
             Request line         8,857K                5K                 <base> ::= "\r\nHost: _HOST_\r\nConnection:close\r\nX-Request-ID:
             Request headers      3,096K                1K                 ↩→ _REQUEST_ID_\r\n"
             Request body         2,051K              595K                 <entity-size-header> ::= <content-length> |
                                                                           ↩→ <chunked-transfer-encoding> |
                                                                           ↩→ <content-length><chunked-transfer-encoding> |
                                                                           ↩→ <chunked-transfer-encoding><content-length>
test the HTTP/2 or HTTP/3 protocols, but our generated requests            <some-header> ::= <accept> | <accept-charset> | (truncated) |
merely appear to use them. As we show, the protocol values still           ↩→ <user-agent> | <via>
                                                                           <body> ::= "\r\nA\r\nBBBBBBBBBB\r\n0\r\n\r\nBBBBB(truncated)"
trigger unexpected parsing behaviors nonetheless.
   Table 3 details what symbols of the grammar are marked as string        (truncated)
or tree mutable. In order to test mutations solely on the request
                                                                                    Listing 10: CFG for request headers experiment.
line, we mark only those symbols as mutable.
5.1.2 Request Headers Experiment Details. Listing 10 details the           <start> ::= <request>
grammar for the request headers experiment. We used all standard           <request> ::=
                                                                           ↩→ <base><entity-size-headers><trailer><chunked-body><padding>
HTTP request headers as detailed in the "Message Headers" reg-
                                                                           <base> ::= "POST /_URI_ HTTP/1.1\r\nHost:
istry of IANA [20], and valid values from their corresponding RFC          ↩→ _HOST_\r\nConnection:close\r\nX-Request-ID: _REQUEST_ID_\r\n"
documents. For a full listing of all 67 headers and values used in         <entity-size-headers> ::= <content-length><transfer-encoding> |
this experiment, refer to Section C in Appendix.                           ↩→ <transfer-encoding>
                                                                           <content-length> ::= "Content-Length: 200\r\n"
    Mutable symbols for this experiment consist of the 67 headers          ↩→ <transfer-encoding> ::= "Transfer-Encoding: chunked\r\n"
and their sub-elements depicted in the full grammar definition.            <trailer> ::= "Trailer: Content-Length\r\n\r\n" | "Trailer:
String literals in the grammar are marked as string mutable, while         ↩→ Transfer-Encoding\r\n\r\n" | "Trailer: Foo\r\n\r\n" | "\r\n"
                                                                           <chunked-body> ::= <chunk><last-chunk><newline> |
all other symbols are marked as tree mutable.
                                                                           ↩→ <chunk><last-chunk><trailer-part><newline>
                                                                           <chunk> ::=
5.1.3 Request Body Experiment Details. Listing 11 depicts the gram-        ↩→ <chunk-size><chunk-extension><newline><chunk-data><newline> |
mar for the request body experiment. This experiment focuses on            ↩→ <chunk-size><newline><chunk-data><newline>
chunked bodies, as they have a complex structure with the highest          <chunk-size> ::= "4"
                                                                           <chunk-extension> ::= ";foo=bar"
potential for parsing discrepancies. We include all chunked body           <chunk-data> ::= "BBBB"
components in the input grammar, namely, the chunk size, chunk ex-         ↩→ <last-chunk> ::= <zero><chunk-extension><newline> |
                                                                           ↩→ <zero><newline>
tension, chunk data, trailer and last chunk. We also add the Trailer
                                                                           ↩→ <zero> ::= "0"
header to the grammar, since it is required to include additional          <trailer-part> ::= "Transfer-Encoding: chunked\r\n" |
fields at the end of chunked messages. Unlike other experiments,           ↩→ "Transfer-Encoding: identity\r\n" | "Content-Length: 180\r\n" |
                                                                           ↩→ "Bar: Foo\r\n"
we fix the method to POST and the version to 1.1.                          <newline> ::= "\r\n"
   The grammar defines a symbol called <padding> which adds                <padding> ::= "DDDDDDDDDD(truncated)"
200 D characters after the last chunk. This symbol lets us deter-
mine whether the experiment server used Transfer-Encoding                          Listing 11: CFG for the request body experiment.
or Content-Length when parsing the body. If the server uses
Content-Length, the output will include our padding values; oth-              Table 4 shows that three chunked body symbols are marked as
erwise, the padding will be omitted from the output, since this is         tree mutable, while the other symbols are string mutable. The rest
the expected behavior for chunked bodies.                                  of the request remains immutable.
                                                                       6
                                                                                 Table 6: Examples for each request line mutation category.
                                                                                  Category             Request Line          Entrypoint-Exitpoint
                                                                                                                                Apache-Akamai
                                                                                    mangled                                       Apache-ATS
                                                                                                   h EAD / HTTP/1.1\r\n
                                                                                    method                                     HAProxy-Akamai
                                                                                                                                 HAProxy-ATS
                                                                                     distorted
                                                                                                   GET / H h TTP/1.1\r\n          ATS-Squid
                                                                                     protocol
                                                                                      invalid                                   ATS-Akamai
                                                                                                   GET / HTTP/ 1 .1 9 \r\n
                                                                                      version                                    ATS-Squid
                                                                                  manipulated       CONNECT / HTTP/1.0         Varnish-NGINX
                                                                                   termination           \r\n \r\n             HAProxy-NGINX
                                                                                    embedded      OPTIONS / HTTP/ OPTIONS
                                                                                                                                Akamai-Squid
                                                                                  request lines    / HTTP/0.9\r\n 1.1\r\n
                                                                                     multiple
                                                                                                   GET / HT T P / /1.1\r\n      HAProxy-Squid
                                                                                    mutations
                                                                                      various                                    Apache-ATS
                                                                                      method                                    HAProxy-ATS
                                                                                                   TRACE / HTTP/ 1.0 \r\n
                                                                                      version                                     Squid-ATS
                                                                                  combinations                                  Varnish-ATS


 Figure 2: Request line mutation categories affecting server pairs.
                                                                             body of the request, while the exitpoint (Akamai or ATS) ignores it.
                                                                             The remaining servers return an error because of the mutation. For
5.2    Stage 2 - Discrepancy Reduction and                                   brevity, in the remainder of this section an error should be assumed
       Classification                                                        if an experiment server is not mentioned explicitly.
We now detail the process for determining mutation success, and                  Distorted Protocol. This category consists of mutation sets that
present our classifications of mutation sets that cause discrepancies.       replace one character in the protocol name with another, usually
                                                                             adding the character h to the beginning of the protocol, or changing
   Successful Mutation Sets. To determine if a mutation from the             the case of an existing letter. Table 6 shows a mutation that causes
previous stage has HRS potential, we first need to define what a             a discrepancy between ATS and Squid. ATS handles the mutation
successful mutation is.                                                      and parses the message body whereas Squid ignores the body.
   A successful mutation set causes a discrepancy in the body pars-              Invalid Version. Mutation sets with this classification add a digit,
ing behavior in at least one server pair, where the absence of the           replace a digit with another, or remove a digit from the beginning
mutation set does not. Essentially, if a mutation set causes a dis-          and adds one to the end. This category of mutations primarily
crepancy that the base unmutated request does not, we consider               involve digits, keeping the versions numeric, yet invalid.
that a successful mutation set.                                                  Manipulated Termination. These mutations primarily add a
   Table 5 shows the number of successful mutations for each ex-             space, tab, or CRLF before the CRLF that terminates the request
periment. To gain deeper insights into the causes and potential for          line. The example in Table 6 triggers a discrepancy when Varnish
HRS, we set out to reduce this set further. We reduce mutation sets          or HAProxy is the entrypoint, and NGINX is the exitpoint. The
based on the following definition.                                           entrypoint determines that the body is what directly follows from
   A mutation set 𝑀2 is reducible to 𝑀1 iff. 𝑀1 ⊆ 𝑀2 and 𝑠 (𝑀2 ) ⊆           the double CRLF, whereas the exitpoint ignores the body completely.
𝑠 (𝑀1 ) where 𝑠 (𝑀) is the set of server pairs which disagree about              Embedded Request Lines. This category includes mutation sets
parsing on an input mutated by a mutation set 𝑀.                             that insert a whole request line into the existing request line at
   Classification of Mutation Sets. We classify mutation sets                various positions, including after the method and the protocol. The
based on their mutation pattern beyond the specifics of how the              example in Table 6 has a request line inserted after the protocol.
mutations are carried out. For example, all mutations deleting, re-              Multiple Mutations. We classify mutation sets into this cate-
placing, or inserting a character in the method of a request line            gory when the individual mutations alone do not trigger a discrep-
follow the same pattern: Distorting Method. We explore these cate-           ancy, but together they do. These mutation sets mainly have two
gories and the server pairs they affect for each experiment below.           forms: 1) the method name is mutated while another CRLF is added
                                                                             next to the terminating CRLF, or 2) a character is deleted from the
5.2.1 Request Line Experiment. Figure 2 lists all mutation cate-             protocol name and a second slash is added after the protocol name.
gories affecting the request line, and the server pairs that disagree            Various Method Version Combinations. Sometimes inconsis-
on body parsing for each category. Table 6 shows examples for each           tent behavior is triggered by bringing various methods and versions
mutation category and the impacted server pairs.                             together with no need for mutations. In the example in Table 6,
   Mangled Method. This class comprises mutation sets which                  ATS ignores the message body for TRACE requests, whereas Apache,
modify the method name. Mutations can change the case of a letter,           HAProxy, Squid, and Varnish do not.
replace the entire method name, or modify it in another way. The
first row in a Table 6 shows an example where a single mutation              5.2.2 Request Headers Experiment. Figure 3 lists all server pairs
causes a discrepancy between 4 different server pairs. We observe            and mutation categories affecting request headers. Table 7 presents
that the entrypoint (Apache or HAProxy) parses and forwards the              examples as before; however, each example shows both the method
                                                                         7
                                                                               Table 7: Examples for each request header mutation category.
                                                                              Category             Method ; Request Header         Entrypoint-Exitpoint
                                                                                                                                      Tomcat-Akamai
                                                                                 distorted         GET;Transfer-Encoding:
                                                                                                                                       ATS-Akamai
                                                                               header value             chunked , \r\n
                                                                                                                                     HAProxy-Akamai
                                                                                                                                       NGINX-ATS
                                                                               manipulated         GET;Transfer-Encoding:
                                                                                                                                      Cloudflare-ATS
                                                                               termination    chunked\r\n ␣ {Header}:{Value}\r\n
                                                                                                                                     CloudFront-ATS
                                                                                 expect                 POST; Expect :                NGINX-Apache
                                                                                 header               100-continue \r\n                 (truncated)
                                                                                                                                     Cloudflare-Squid
                                                                                 identity         POST; Transfer-Encoding :
                                                                                                                                     CloudFront-Squid
                                                                                encoding                identity \r\n
                                                                                                                                        (truncated)
                                                                               v1.0 chunked       POST;Transfer-Encoding:             Apache-Tomcat
                                                                                 encoding                chunked \r\n                   (truncated)
                                                                                                  POST;Transfer-Encoding:           Cloudflare-Tomcat
                                                                                 double
                                                                                                        identity\r\n                CloudFront-Tomcat
                                                                                transfer-
                                                                                                    Transfer-Encoding:              Cloudflare-Akamai
                                                                                encoding
                                                                                                         chunked \r\n                   (truncated)
                                                                                various
                                                                                method                  OPTIONS-0.9 ;                 HAProxy-Squid
                                                                                version             Content-Length:5\r\n              Akamai-Squid
                                                                              combinations




Figure 3: Request header mutation categories affecting server pairs.
                                                                                Various Method Version Combinations. Similar to the request
                                                                             line experiment, various options for methods and versions defined
name and the mutation, since successful mutations in this experi-            in the input grammar are combined in different ways to generate
ment vary from method to method.                                             our input requests. In this experiment, these combinations are also
   Distorted Header Value. This category includes mutation sets              combined with various headers including Transfer-Encoding, and
that add specific characters, such as a vertical tab, new page, space,       can cause discrepancies without a mutation.
plus, and comma to the beginning and end of specific header values.
The headers in this category are Transfer-Encoding: chunked                  5.2.3 Request Body Experiment. Figure 4 lists the server pairs that
and Content-Length: LENGTH. In Table 7, a comma is appended to               have discrepancies with mutation categories affecting the request
the header value. Akamai ignores the request body, while Tomcat,             body, and Table 8 shows examples.
HAProxy and ATS parse and forward it.                                           Chunk-Size Chunk-Data Mismatch. These mutations add
   Manipulated Termination. Mutations in this category mainly                to or remove a character from chunk data to make its size differ-
insert a space or tab after the header-terminating CRLF, resulting           ent from what is claimed in the chunk size. This causes Akamai
in parsing discrepancies. In Table 7, a space is added after the CRLF        to process the request body using Content-Length and ignore
following the first header value. As a result, ATS ignores the request       Transfer-Encoding, while every other server (except Tomcat and
body, whereas NGINX, Cloudflare and CloudFront do not.                       Apache that give an error) continues to use Transfer-Encoding.
   Expect Header. We find that the Expect header is interpreted                 Manipulated Chunk-Size Termination. Mutations in this cate-
differently by Apache. When Apache receives a request with this              gory modify the CRLF terminating the chunk size, and typically add
header and its 100-continue value, it ignores the body in the                a character such as a new page, semicolon, or space. This causes Aka-
request as opposed to every other server we experimented with.               mai to make a different preference between Transfer-Encoding
   Identity Encoding. When a request has a Transfer-Encoding                 and Content-Length headers compared to the other servers.
header with the identity value, Squid and ATS ignore the mes-                   Manipulated Chunk-Extension Termination. In this cate-
sage body. Tomcat, Akamai, Cloudflare and CloudFront parse and               gory, mutation sets remove a part of the newline which termi-
forward the body.                                                            nates the chunk extension. Usually, the carriage return character
   V1.0 Chunked Encoding. Tomcat does not support chunked                    is deleted. Again, this causes Akamai to use the Content-Length
encoding in HTTP version 1.0. Thus, this causes an inconsistency             header instead of the Transfer-Encoding header.
between Tomcat and all other servers we experimented with. When                 Manipulated Chunk-Data Termination. These mutations
a request has both Transfer-Encoding and Content-Length head-                remove the terminating CRLF, partially or wholly from the chunk
ers, all servers prefer the former, whereas Tomcat prefers the latter.       data part of the request. In the example shown in Table 8, the CRLF
   Double Transfer-Encoding. We observe an interesting behavior              is completely removed at the end of the first chunk data.
when a request has two Transfer-Encoding headers. When the first                Mangled Last-Chunk. Mutations in this category include re-
header has the value identity and the second chunked, Cloudflare             moving one CRLF before the last chunk, inserting digits next to the
and CloudFront use the Content-Length header, while Tomcat,                  chunk size in the last chunk (as seen in Table 8), or removing the
HAProxy, and Akamai use Transfer-Encoding to dictate message                 entire last chunk itself. Unlike HAProxy, Squid, and CloudFront,
body parsing.                                                                Akamai does not treat the request body as chunked-encoded.
                                                                         8
                                                                                    Table 8: Examples for each request body mutation category.
                                                                                       Category            Request Body        Entrypoint-Exitpoint
                                                                                          chunk-size                              Akamai-NGINX
                                                                                                             4\r\n B BBB
                                                                                         chunk-data                              Akamai-Varnish
                                                                                                           \r\n0\r\n\r\n
                                                                                           mismatch                                 (truncated)
                                                                                         manipulated                            Cloudflare-Akamai
                                                                                                             4 \t \nBBBB
                                                                                          chunk-size                               Squid-Akamai
                                                                                                           \r\n0\r\n\r\n
                                                                                         termination                                (truncated)
                                                                                         manipulated                            Akamai-Cloudflare
                                                                                                         4;foo=bar \r \nBBBB
                                                                                       chunk-extension                              Akamai-ATS
                                                                                                           \r\n0\r\n\r\n
                                                                                         termination                                (truncated)
                                                                                         manipulated      4\r\nBBBB \r\n 4      CloudFront-Varnish
                                                                                         chunk-data          \r\nBBBB            Akamai-Varnish
                                                                                         termination       \r\n0\r\n\r\n            (truncated)
                                                                                                                                 HAProxy-Akamai
                                                                                           mangled           4\r\nBBBB
                                                                                                                                   Squid-Akamai
                                                                                          last-chunk      \r\n 2 0\r\n\r\n
                                                                                                                                    (truncated)



                                                                                 the others failed. We note that for all but two specific cases, the pay-
                                                                                 load format allowed with these HRS attack vectors is unrestricted.
    Figure 4: Request body mutation categories affecting server pairs.           For the following, please reference Figure 5 for the server combi-
                                                                                 nations and mutation categories that successfully carried an HRS
                                                                                 payload, and Figure 6 for a breakdown of the server combinations
    5.3    Stage 3+ - Determining Discrepancy HRS
                                                                                 and reasons mutations failed for them.
           Potential
    Stage 2 yielded classified mutation sets that are reduced to represen-       5.3.1 Request Line Mutations. Among the request line mutations,
    tative examples, and the server pairs that have parsing discrepancies        only two categories failed to carry out HRS, Distorted Protocol,
    on said sets. We now determine if these parsing discrepancies can            and Multiple Mutations. We found that some servers normalized
    be used for HRS. For every unique server pair that appears in the re-        parts of the request line before forwarding them to the next server
    sults from Stage 2, we set up our lab to position them as entrypoint         when they encountered our mutations, or just flat out closed the
    and exitpoint on path. We ensure that the connection between the             connection. We observed servers being particularly sensitive to
    two servers is persistent, as HRS requires this.                             invalid requests, which is very common in these two mutation
       To understand if our mutated requests lend themselves to HRS,             categories. The successful categories contained less invalidating
    we craft a smuggler request as shown in Listing 12 for every muta-           mutations, and thus proved more fruitful.
    tion (Listing 12 shows a Mangled Method mutation). Immediately                  In one case, there was a restriction on the format of the smug-
    after we send the smuggler request, we send a benign request like            gled content. Using a specific method version combination with
    Listing 13 on the same connection. If our smuggler request was               Squid-Akamai, the entrypoint expected the request to be in chun-
    successful, our payload (shown in Listing 14) will invalidate the            ked encoding. Therefore, the smuggled content had to follow the
    valid request, and the exitpoint will return a 400 Bad Request               chunked format.
    error message in a response to the benign request.                              Unlike others, Varnish cleans its connection, preventing HRS.
       To ensure the error message did not come from the entrypoint,             When Varnish receives a GET request with a body, even though it
    we note that each server has their own unique fingerprint in the             ignores the body, it does not leave the body in the connection.
    HTML error page returned, and verify the exitpoint’s fingerprint.            5.3.2 Header Mutations. We observe that the categories Manip-
       For each experiment, we now detail the categories of mutations            ulated Termination, Expect Header, Identity Encoding, and Double
    that can be used for HRS, and discuss why they succeeded while               Transfer-Encoding failed to work in any server combination. Similar
                                                                                 to the request line mutations, mutations to the Transfer-Encoding
1 h EAD / HTTP/1.1                    1 POST / HTTP/1.1                          and Content-Length headers often were not preserved. Servers
2 Host: example.com                   2 Host: example.com                        typically re-wrote their own headers in place of our mutations
3 Content-Length: 2                   3 Content-Length:5                         based off of what they parsed, effectively stopping all HRS. For the
4                                     4                                          Expect Header failure, that category only affected Apache, and
5   A␣                                5   AAAAA                                  Apache closes the connection after receiving these requests.
                                                                                    Similar to the request line experiment, only one case restricted
      Listing 12: Smuggler request.         Listing 13: Benign request.
                                                                                 the format of the smuggled content. When a request with a distorted
                    1 A␣ POST / HTTP/1.1                                         header value was sent to ATS-Akamai, ATS required the smuggled
                    2 Host: example.com                                          content to be in chunked encoding.
                    3 Content-Length:5

                    4                                                            5.3.3 Body Mutations. For failed HRS attempts using body mu-
                    5   AAAAA                                                    tations, the entrypoint re-wrote the mutated chunked body and
                                                                                 therefore did not preserve the mutation. In all cases where Akamai
                         Listing 14: Poisoned request.                           was an entrypoint, HRS attempts succeeded except for Akamai-ATS.
                                                                             9
         (a) Pairs affected by line mutations.              (b) Pairs affected by header mutations.            (c) Pairs affected by body mutations.
                                                  Figure 5: Server pairs affected by request smuggling.


ATS ignored the message body part that follows the last chunk as                   1.0. Tomcat ignores this header, presumably because it assumes that
expected, yet it did not leave that part in the connection.                        requests with version 1.0 cannot use chunked encoding. As a result,
                                                                                   the body of the request shown in Listing 16 is ignored by Tomcat,
5.3.4 Reasoning about Discrepancies. Server developers typically                   while it is parsed by HAProxy.
base their design decisions on official documents like HTTP RFCs.                     Body Mutations. Listing 17 terminates the chunk extension
Unfortunately, RFCs cannot accommodate information about how                       with an LF rather than a CRLF. NGINX treats the LF the same as
to interpret every single iteration of a valid HTTP request. When                  a CRLF and parses the message body as chunked. However, Aka-
confronted with gray areas, developers have to make implementa-                    mai handles this error by defaulting to Content-Length instead of
tion decisions that conform to the RFC, but are not explicit. HRS                  Transfer-Encoding to parse the message body.
arises from this gray area. We illustrate this by providing exam-
ples from each part of the HTTP request in our experiments that
successfully lead to HRS.
                                                                                   6     IMPACT ASSESSMENT
   Request Line Mutations. In Listing 15, the version in the re-                   So far we have answered our core research questions and systemat-
quest line is mutated. Despite this mutation, ATS still forwarded                  ically confirmed that HTTP processing discrepancies lead to novel
the message body to an exitpoint. In fact, we observed that ATS                    HRS vulnerabilities. Next, we present a set of empirical experiments
forwarded the message body for all GET requests with any decimal                   to reaffirm that these vulnerabilities in fact have practical impact,
version number (i.e., 99.99). Conversely, this mutation caused Squid               and compare our work to existing HRS testing tools.
to ignore the message body, presumably because it could not decide
what the version is. Squid’s body parsing behavior is dependent on                 6.1     Demonstrating Possible Attacks
the version, as it ignores request bodies in version 0.9, yet accepts              The damage caused by an HRS attack depends on the web applica-
them in newer versions.                                                            tion and data exposed by the vulnerable server pair. In this paper,
   Header Mutations. Listing 16 shows a request which uses chun-                   we do not quantify such damage. Instead, we explore the discrepan-
ked encoding with HTTP version 1.0, even though the chunked                        cies between HTTP processors and quantify the HRS attack surface
encoding was introduced to the protocol with version 1.1. Despite                  independent of the outcome of any particular exploitation scenario.
this fact, HAProxy supports chunked encoding in HTTP version                          Regardless, to demonstrate end-to-end attacks in a proof-of-
                                                                                   concept, we set up an environment with a vulnerable application
                                                                                   behind a server pair with actionable discrepancies. Specifically,
 1 GET / HTTP/ .11                 1 POST / HTTP/1. 0                              we abused a chunked body parsing discrepancy between Akamai-
 2 Host: example.com               2 Host: example.com                             NGINX. We configured NGINX to serve OWASP Mutillidae [8], a
 3 Content-Length: 5               3 Transfer-Encoding: chunked
                                                                                   deliberately vulnerable web application for security training.
 4                                 4
                                                                                      We tested three scenarios using HRS: 1) bypassing header rewrit-
 5   AAAAA                         5   4\r\nBBBB\r\n0\r\n\r\n
                                                                                   ing, 2) hijacking requests, and 3) delivering attack payloads. In (1),
      Listing 15: Line mutation.       Listing 16: Header and version.             we smuggled a request with an arbitrary X-Forwarded-For value,
                                                                                   evading re-writing by the entrypoint. This is critical, since this
                 1 POST / HTTP/1.1
                                                                                   header is often used in authentication and authorization schemes [26].
                 2 Host: example.com
                 3 Content-Length: 5
                                                                                   In (2) our smuggler request payload constructed a poisoned request
                 4 Transfer-Encoding: chunked
                                                                                   to an attacker-controlled destination, leaking a random user’s re-
                 5                                                                 quest content including session cookies. Finally, in (3) we smuggled
                 6   4;foo=bar \r \nBBBB\r\n0\r\n\r\n                              a request which exploits a reflected XSS vulnerability at the desti-
                                                                                   nation to have the XSS response delivered to a random user. Videos
                        Listing 17: Body mutation.                                 of these attacks in action are available on the authors’ websites.
                                                                              10
        (a) Pairs affected by line mutations.                    (b) Pairs affected by header mutations.             (c) Pairs affected by body mutations.
                                             Figure 6: Failed request smuggling reasons for each server pair.


6.2    Estimating Server Combinations                                                   may use distinct proxy technologies, requiring a comprehensive
At a first glance, some of the server pairs we test may seem unreal-                    crawl of each site for an accurate analysis. Finally, a blackbox de-
istic for a real-life deployment scenario. However, the Internet has                    tection methodology cannot determine the placement order of the
become a complex ecosystem (and patchwork) of middle-boxes and                          servers, but only the fact that they are used in some combination.
cloud services, where any given request may be processed by not                         These are non-trivial challenges that we do not tackle in this work.
two, but many servers. CDN deployments are prevalent [2], and                              Figure 7 summarizes our results, showing pairwise server com-
multi-CDN chaining is practical [21]. These services themselves                         binations we observed, where the edge weights represent the inci-
may depend on popular proxies, web caches, and web servers (e.g.,                       dence. We find that approximately 17% of the sites among the top
Fastly uses Varnish [25], Cloudflare uses NGINX [30]).                                  10K use technologies that we have identified discrepancies between.
    To illustrate our point, we conducted an experiment with the top                    We observe an average of 2.8 technologies, with a median of 3, and
10K sites of the Tranco list [34], exploring what server technolo-                      a maximum of 5 per site.
gies are deployed in the wild. Namely, we visited the homepage of                          Given the aforementioned limitations, these results represent
each site and collected the HTTP response headers. We simultane-                        a loose lower bound on the incidence of server pairings. Yet, they
ously ran route traces for IP addresses seen on path, and performed                     show two important points. First, out of the 45 possible combina-
WHOIS lookups for each. We then searched through this data for                          tions of the 10 servers in our setup, 36 are used in the wild. Second,
known header & value combinations that fingerprint the technolo-                        seemingly unrealistic combinations are viable, and chained CDNs
gies, and for explicit service identifiers inside HTTP responses,                       are more frequent than other combinations. We conclude that mak-
WHOIS data, and email domains. This process resulted in a set of                        ing presumptions about what server combinations are viable in
potential server technologies used for each site.                                       the wild is counterproductive when exploring HRS and similar
    This methodology has limitations. There is no known way to re-                      systems-level hazards. Processing discrepancies can crop up on any
liably detect proxy services via traffic analysis, particularly because                 technology, and therefore, all combinations are worth investigating.
many services allow operators to strip the identifying headers to
prevent fingerprinting. Furthermore, different endpoints on a site                      6.3     Comparing T-Reqs to Existing Tools
                                                                                        James Kettle’s Burp Suite extension HTTP Request Smuggler [36]
                                                                                        and Evan Custodio’s Python script smuggler [7] are the state-of-
                                        Squid
                                                                                        the-art tools used when testing sites for HRS.
                     Varnish                             HAProxy                           Foremost, both of these operate on fundamentally different tar-
                                                                                        gets and serve a different purpose than T-Reqs. In particular, these
                                                                                        tools are designed for penetration testing of a given target site,
            Apache       Akamai                    CloudFront                           treating the entire web deployment as a blackbox, and testing it for
                                                                                        a set of known Content-Length and Transfer-Encoding header
                                                                                        manipulation attacks presented in the authors’ respective works.
                                      Cloudflare                                           In contrast, T-Reqs is not designed to test live sites against known
                      NGINX                                ATS                          exploits. T-Reqs tests pairwise combinations of HTTP processors in
                                       Tomcat
                                                                                        a lab environment, and exercises each component individually, in
                                                                                        a greybox manner. It is designed to discover novel HRS vectors, as
Figure 7: HTTP processors paired in the wild. This is an unordered                      opposed to testing a real-life deployment for known attacks.
graph, showing pairwise combinations. Red edges indicate pairs that                        Therefore, these tools are not substitutes for each other. T-Reqs
exhibit processing discrepancies, blue edges represent pairs that do                    serves to discover novel HRS payloads that the others are bound to
not. Edge thickness corresponds to the incidence of pairs.                              miss given their limited scope. That does not diminish the value
                                                                                   11
    of previous work. In fact, these tools are suited to work in tandem,                  While this could be an oversight in design, it is also possible that
    where T-Reqs finds novel exploits, which can then be added to Burp                 Akamai’s behavior has changed in the two years since the publica-
    or smuggler to automate their use in penetration testing.                          tion of Kettle’s work. We conclude that testing the existing tools in
        With that in mind, we next describe how these existing tools                   a large-scale experiment is not safe without explicit penetration-
    work, and present an empirical study demonstrating T-Reqs’s ability                testing agreements. Instead, we conducted our comparative study
    to create new knowledge for HRS research.                                          in the same test environment used for the experiments with T-Reqs.
        HRS Detection in Existing Tools. Existing tools use the detec-                    Empirical Comparison. We have already presented T-Reqs’s
    tion methodology proposed by Kettle [15]. First, the request in List-              output in Section 5. To compare those results with the detections
    ing 18 is sent to a target to determine if it is affected by a CL.TE dis-          from the two existing tools, we ran them in the same experimental
    crepancy, meaning the entrypoint processes the Content-Length                      setup. However, there were two cases we could not test. It is not
    header while the exitpoint prefers Transfer-Encoding. If the tar-                  possible to run Tomcat in a reverse-proxy mode, and therefore we
    get is vulnerable, the entrypoint will forward the first four bytes                did not test pairs having Tomcat as the entrypoint. Additionally,
    (i.e., chunk size 1 and chunk data Z), and the exitpoint will timeout              we were unable to set up a Cloudflare-CloudFront pair, because the
    waiting for the next chunk which will never arrive. This timeout                   Host header rewriting capability necessary for that deployment is
    delay flags the site as vulnerable.                                                only available for Cloudflare’s Enterprise plan customers [3]. This
        If the CL.TE test fails, the request in Listing 19 is sent to check            limitation was not a factor during our core experiments with T-Reqs;
    for a TE.CL discrepancy. If there is a vulnerability, a similar delay              we had found no discrepancies for this CDN pair, and therefore we
    can be observed: The entrypoint forwards the body without the                      did not need to attempt a deployment for exploitability testing.
    byte X that comes after the last chunk, and the exitpoint receives                    Unsurprisingly, T-Reqs was the only tool that found the request
    less content than what Content-Length indicates, therefore timing                  line and request body related attacks described in Sections 5.2.1
    out while waiting for one additional byte.                                         and 5.2.3. The others missed this category of attacks entirely, be-
        As also emphasized by Kettle, the order of the above two checks                cause they are only designed to test for the Content-Length and
    is important. The TE.CL test should only be performed after con-                   Transfer-Encoding header manipulation attacks. One exception
    firming the absence of a CL.TE discrepancy. Otherwise the TE.CL                    was that Burp found an exploitable Mangled Last-Chunk discrep-
    request could poison the connection with the byte X in CL.TE-                      ancy on Akamai-ATS. We manually verified that this was an acciden-
    impacted targets, launching an attack on arbitrary Internet users.                 tal true positive, as the request template Burp uses unintentionally
        All in all, both tools iterate through numerous mutations in the               had the trigger for this discrepancy built into the chunked body –
    Transfer-Encoding header of the requests in Listings 18 and 19,                    no mutations were necessary for this finding. T-Reqs also detected
    and check the target for CL.TE and TE.CL discrepancies using the                   the same vulnerability through body mutations.
    above methodology.                                                                    An unanticipated outcome was neither smuggler nor Burp flagged
        Safety of the Detection Methodology. Unfortunately, we have                    any request header attacks either, even though they are designed
    empirically confirmed in our tests that neither tool is currently safe             to test for those. We reviewed the source code for both tools and
    to run on real-world targets.                                                      verified that the header modifications they use1,2 indeed do not lead
        Firstly, Custodio’s smuggler does not follow the above order of                to any exploitable HRS vulnerabilities today on the 10 technologies
    requests which is critical for preventing inadvertent attacks. More                in our setup. We attribute this to the fact that these tools repeat
    interestingly, even though Burp follows the protocol, we have deter-               known exploits, and the server vendors have already had two years
    mined that a false assumption made in the detection methodology                    to implement mitigations since their disclosure.
    makes it hazardous for running real-world experiments. Namely,                        In summary, Burp detected one HRS vulnerability and smuggler
    the methodology assumes that, when the target is affected by a                     detected none, whereas T-Reqs yielded all the findings we presented
    TE.CL discrepancy, the entrypoint will treat the byte Q in Listing 18              in Section 5. We conclude that T-Reqs indeed fulfills its role of
    as an invalid chunk size and return an error. That will prevent ac-                finding novel HRS vectors left out of scope in previous work.
    cidentally poisoning the connection during the CL.TE check. This
    assumption does not hold; Akamai servers do not return an error                    6.4     Testing HRS in the Wild
    and forward the request as if Q was a proper chunk size.                           Due to the aforementioned safety issues, the state-of-the-art HRS
                                                                                       detection methodology should not be used outside of specific tar-
                                                                                       gets that explicitly allow external testing of their sites. Designing a
                                                                                       safe detection scheme likely requires whitebox analysis of different
1 POST / HTTP/1.1                       1 POST / HTTP/1.1
                                                                                       HTTP processors to ensure that request queues are not inadver-
2 Host: example.com                     2 Host: example.com                            tently poisoned. Conducting a large-scale HRS measurement in the
3 Transfer-Encoding: chunked            3 Transfer-Encoding: chunked                   wild safely is a promising future research direction.
4 Content-Length: 4                     4 Content-Length: 6                               Here, we instead present a preliminary experiment testing real-
5                                       5                                              world deployments that has Akamai as entrypoint, and only with
6 1                                     6   0                                          a specific HRS payload. This is not an arbitrary choice. Prior to
7 Z                                     7
8 Q                                     8   X                                          1 https://github.com/PortSwigger/http-request-smuggler/blob/master/src/burp/
                                                                                       DesyncBox.java
      Listing 18: CL.TE test request.           Listing 19: TE.CL test request.        2 https://github.com/defparam/smuggler/blob/master/configs/exhaustive.py

                                                                                  12
running the experiment, we carefully studied Akamai’s behavior                   We stress that our findings should not be taken at face value.
and crafted an HRS detection payload, based on Kettle’s approach,             This work is not intended to be a prescribed list of vulnerabilities
that is guaranteed to be safe for this particular experiment.                 and their mitigations. We provide strong indicators for hazardous
    Specifically, we used a body mutation in the Chunk-Size Chunk-            server combinations and demonstrate the severity of the issue, so
Data Mismatch category. Recall from Section 5.2.3 and Figure 4 that           that system owners can vet their environments.
this category impacts server pairs that has Akamai as entrypoint,                Blame Nobody. We reiterate that HRS is a system interaction
and always makes Akamai prefer the Content-Length header. That                problem. Individual components of the system are not necessarily
enables us to test sites for this novel HRS vulnerability while ac-           flawed, but their hazardous combination results in a vulnerability
tively avoiding the unsafe situation, where an Akamai server prefers          that is not trivial to detect or mitigate. This implies that technology
the Transfer-Encoding but allows an invalid chunk size through.               vendors are not always in a position to correct these flaws on their
    To make sure that Akamai is the entrypoint in the target, we              own; an ideal HTTP processor that is strictly RFC compliant, using
sent a TRACE request with the Max-Forwards:0 header to force                  a formally-verified parser, and implemented by the best developers
the request to stop at the first HTTP server on path even if it does          on the planet may still get caught in an HRS attack when combined
not support the TRACE method. Out of 861 Akamai customers from                with a different technology that interprets a request differently.
Tranco Top 10K identified previously in Section 6.2, we were able             Unfortunately, the reality is even more complicated, where RFCs
to confirm 367 had Akamai as the entrypoint.                                  are ambiguous, bugs are inevitable, and powerful mechanisms to
    We tested these 367 domains by sending our new HRS detection              rewrite HTTP requests are desirable and necessary features. In this
request, and flagged sites as vulnerable if they did not respond              complex ecosystem, predicting, detecting, mitigating, or fixing HRS
within 5 seconds (i.e., the default threshold used in existing tools).        is a non-trivial, open research problem.
Out of the 367 domains tested, we found 23 to be vulnerable. These               While the results we present in this paper may appear to show
included a high-profile financial institution, online retailers, and          that some technologies and vendors are better than others, that
other technology, news, and entertainment sites.                              is an incorrect interpretation of our results. Our findings do not
    This experiment is decidedly narrow in scope. However, it suc-            represent a meaningful security comparison between the tested
cessfully demonstrates that real-world deployments are exposed to             servers, and they should not be taken out of context to pit one tech-
HRS vulnerabilities we discovered with T-Reqs, despite the many               nology against another. Once again, this work presents a scientific,
hidden layers of complexity present in the wild that we could not             systematic methodology to identify HRS, and uncover previously
account for in a lab environment. Designing a generalizable detec-            unexplored venues for attacks, so that the developers and users of
tion methodology and enabling a full-fledged measurement study                these technologies are better equipped to understand the implica-
is the logical next step for characterizing the impact of HRS.                tions of the issue, and vet their own systems.

                                                                              8    CONCLUSION
                                                                              This paper is the first systematic exploration of HRS attacks. Re-
7   DISCUSSION
                                                                              visiting our research questions from Section 3, we proposed an
As we conclude, we underscore considerations for the correct in-              experiment infrastructure and methodology for efficient discovery
terpretation of our results.                                                  of attacks (Q1), developed a novel grammar-based differential fuzzer
   Limitations. While this paper represents the most holistic in-             to test all components of an HTTP request for viable exploits (Q2),
vestigation into HRS to date, it is by no means exhaustive. For               provided insights into previously unknown success (and failure)
example, we leave non-standard HTTP headers out of scope. There               modes enabled by our exploits (Q3), and finally documented haz-
are further restrictions we impose on our methodology to make                 ardous combinations of popular servers (Q4). Our findings collec-
the experimentation and analysis feasible, such as limiting the max-          tively show that HRS may yet evolve into an even more complex
imum number of mutations for an input, and mutating request                   attack, and it is paramount that the security community tackle the
components in isolation in their respective experiment runs.                  open research questions in the areas of detection and defense.
   Nonetheless, our approach provides sufficient evidence to ad-
dress our research questions, showing that there are indeed vast
                                                                              ACKNOWLEDGMENTS
and unexplored opportunities for crafting HRS attacks – and that
the security community must stay alert. We make T-Reqs available              This work was supported by the National Science Foundation under
in the hopes that our fellow researchers will improve on it and               grant CNS-1703454 and by Secure Business Austria.
make even more exciting discoveries.
   Real-World Considerations. In our experiments we test all                  REFERENCES
servers in their default configurations. While all of the exploits we          [1] Cornelius Aschermann, Tommaso Frassetto, Thorsten Holz, Patrick Jauernig,
                                                                                   Ahmad-Reza Sadeghi, and Daniel Teuchert. 2019. NAUTILUS: Fishing for Deep
find are real and practical, configurations will vary considerably                 Bugs with Grammars. In The Network and Distributed System Security Symposium.
in the wild. What is more, servers that we flag as impacted in this            [2] BuiltWith. [n.d.]. BuiltWith Technology Lookup. https://trends.builtwith.com/
                                                                                   CDN/Content-Delivery-Network.
experiment may be deployed behind other proxies (e.g., a web appli-            [3] Cloudflare Help Center. 2021. Using Page Rules to Re-Write Host Head-
cation firewall or load balancer between the entry and exitpoints),                ers. https://support.cloudflare.com/hc/en-us/articles/206652947-Using-Page-
which intentionally or inadvertently strip out exploit payloads. On                Rules-to-Re-Write-Host-Headers.
                                                                               [4] Jianjun Chen, Jian Jiang, Haixin Duan, Nicholas Weaver, Tao Wan, and Vern Pax-
the flip side, non-default configurations may also expose dangerous                son. 2016. Host of Troubles: Multiple Host Ambiguities in HTTP Implementations.
discrepancies that we were not able to catch in our study.                         In ACM Conference on Computer and Communications Security.
                                                                         13
 [5] Evan Custodio. 2019. Mass account takeovers using HTTP Request Smuggling                               Table 9: Tested HTTP servers and versions.
     on https://slackb.com/ to steal session cookies. https://hackerone.com/reports/                           HTTP Server                         Tested Version
     737140.
 [6] Evan Custodio. 2020. Practical Attacks Using HTTP Request Smuggling by                                    Apache                                       2.4.46
     @defparam. NahamCon. https://www.youtube.com/watch?v=3tpnuzFLU8g.                                         NGINX                                        1.18.0
 [7] Evan Custodio. 2020. Smuggler. https://github.com/defparam/smuggler.                                      Tomcat                                       9.0.40
 [8] Jeremy Druin. 2021. OWASP Mutillidae II. https://github.com/webpwnized/                                   Apache Traffic Server (ATS)                   8.1.1
     mutillidae.                                                                                               HAProxy                                       2.3.1
 [9] Roy Fielding, James Gettys, Jeff Mogul, Henrik Frystyk, and Tim Berners-Lee.                              Squid                                          4.13
     1997. Hypertext Transfer Protocol – HTTP/1.1. https://tools.ietf .org/html/                               Varnish                                       6.0.7
     rfc2068.                                                                                                  Akamai                                         N/A
[10] Roy Fielding and Julian Reschke. 2014. Hypertext Transfer Protocol (HTTP/1.1):                            Cloudflare                                     N/A
     Message Syntax and Routing. https://tools.ietf .org/html/rfc7230.                                         CloudFront                                     N/A
[11] Roy Fielding and Julian Reschke. 2014. Hypertext Transfer Protocol (HTTP/1.1):
     Semantics and Content. https://tools.ietf .org/html/rfc7231.
[12] Omer Gil. 2017. Web Cache Deception Attack. Black Hat USA.                https:
     //www.blackhat.com/us-17/briefings.html#web-cache-deception-attack.                     [37] Suphannee Sivakorn, George Argyros, Kexin Pei, Angelos D. Keromytis, and
[13] Omer Gil. 2017. Web Cache Deception Attack. https://omergil.blogspot.com/                    Suman Jana. 2017. HVLearn: Automated Black-Box Analysis of Hostname Verifi-
     2017/02/web-cache-deception-attack.html.                                                     cation in SSL/TLS Implementations. In IEEE Security & Privacy.
[14] Patrice Godefroid. 2020. Fuzzing: Hack, Art, and Science. Commun. ACM 63, 2             [38] Andreas Zeller, Rahul Gopinath, Marcel Böhme, Gordon Fraser, and Christian
     (2020).                                                                                      Holler. 2019. The Fuzzing Book. In The Fuzzing Book. Saarland University.
[15] James Kettle. 2019. HTTP Desync Attacks: Request Smuggling Reborn. PortSwig-                 https://www.fuzzingbook.org/.
     ger Web Security Blog. https://portswigger.net/blog/http-desync-attacks-
     request-smuggling-reborn.
[16] James Kettle. 2019. Password theft login.newrelic.com via Request Smuggling.            A     TESTED HTTP PROCESSORS
     HackerOne. https://hackerone.com/reports/498052.
[17] James Kettle. 2019. Stored XSS on https://paypal.com/signin via cache poisoning.        We experiment with 10 popular HTTP processors in this work, us-
     HackerOne. https://hackerone.com/reports/488147.                                        ing the latest stable versions available at the time of writing. Table 9
[18] James Kettle. 2021. HTTP/2: The Sequel is Always Worse. Black Hat
     USA. https://www.blackhat.com/us-21/briefings/schedule/#http2-the-sequel-is-
                                                                                             shows specific versions of each technology, with the exception of
     always-worse-22668.                                                                     CDNs which do not have public release labels.
[19] Amit Klein. 2020. HTTP Request Smuggling in 2020 – New Variants, New
     Defenses and New Challenge. Black Hat USA. https://www.blackhat.com/us-
     20/briefings/schedule/#http-request-smuggling-in---new-variants-new-                    B     FORMAL MUTATION DEFINITIONS
     defenses-and-new-challenges-20019.                                                      T-Reqs uses string and tree mutations to generate HTTP requests.
[20] Graham Klyne. 2021. Message Headers. https://www.iana.org/assignments/
     message-headers/message-headers.xhtml.                                                  Here, we provide formal definitions for mutation operations.
[21] Dima Kumets. 2019. 8 best practices for multi-CDN implementations. https:
     //www.fastly.com/blog/best-practices-multi-cdn-implementations.
[22] Emil Lerner. 2021. http2smugl. https://github.com/neex/http2smugl.
                                                                                             String mutation operations.
[23] Regis Leroy. 2016. Hiding Wookiees in HTTP: HTTP smuggling. DEF CON.                       Given:
     https://www.youtube.com/watch?v=dVU9i5PsMPY.
[24] Chaim Linhart, Amit Klein, Ronen Heled, and Steve Orrin. 2005. HTTP Re-                      • 𝑁 is a set of all non-terminal symbols in a CFG.
     quest Smuggling. Watchfire. https://www.cgisecurity.com/lib/HTTP-Request-                    • 𝑇 is a set of all terminal symbols in a CFG.
     Smuggling.pdf.
[25] Anna MacLachlan. 2015. The benefits of using Varnish. https://www.fastly.com/
                                                                                                  • 𝐵 is a predefined character pool.
     blog/benefits-using-varnish.                                                            Let 𝑠 be a string mutable symbol represented by a CFG as
[26] Lori MacVittie. 2017. Security Rule Zero: A Warning about X-Forwarded-
     For. https://www.f5.com/company/blog/security-rule-zero-a-warning-about-                                                  𝑠 ::= 𝑡 1 |𝑡 2 |...|𝑡𝑘
     x-forwarded-for.
[27] William M. McKeeman. 1998. Differential Testing for Software. Digital Technical
     Journal 10, 1 (1998).
                                                                                             where 𝑠 ∈ 𝑁 , and 𝑡𝑖 ∈ 𝑇 .
[28] Seyed Ali Mirheidari, Sajjad Arshad, Kaan Onarlioglu, Bruno Crispo, Engin Kirda,           Given the expansion of 𝑠 → 𝑡 1 , where 𝑡 1 is a sequence of 𝑛
     and William Robertson. 2020. Cached and Confused: Web Cache Deception in                characters 𝑐 1𝑐 2 ...𝑐𝑛 , a string mutator is represented as a function
     the Wild. In USENIX Security Symposium.
[29] Seyed Ali Mirheidari, Matteo Golinelli, Kaan Onarlioglu, Engin Kirda, and Bruno         𝑓 (𝑡 1, 𝑜𝑝, 𝑗, 𝑏) where 𝑜𝑝 ∈ {delete-char, replace-char, insert-char},
     Crispo. 2022. Web Cache Deception Escalates!. In USENIX Security Symposium.             1 ≤ 𝑗 ≤ 𝑛, and 𝑏 ∈ 𝐵:
[30] NGINX. [n.d.]. Cloudflare boosts performance and stability for its millions of
     websites with NGINX. https://www.nginx.com/success-stories/cloudflare-                                               𝑐 ...𝑐 𝑐 ...𝑐 ,               if 𝑜𝑝 = 𝑑𝑒𝑙𝑒𝑡𝑒-𝑐ℎ𝑎𝑟
                                                                                                                          1 𝑗−1 𝑗+1 𝑛
                                                                                                                         
                                                                                                                         
     boosts-performance-stability-millions-websites-with-nginx/.
                                                                                                                         
                                                                                                                         
[31] Hoai Viet Nguyen, Luigi Lo Iacono, and Hannes Federrath. 2019. Your Cache                 𝑓 (𝑐 1 ...𝑐𝑛 , 𝑜𝑝, 𝑗, 𝑏) = 𝑐 1 ...𝑐 𝑗−1𝑏𝑐 𝑗+1 ...𝑐𝑛 ,    if 𝑜𝑝 = 𝑟𝑒𝑝𝑙𝑎𝑐𝑒-𝑐ℎ𝑎𝑟
     Has Fallen: Cache-Poisoned Denial-of-Service Attack. In ACM Conference on                                           
                                                                                                                                                        if 𝑜𝑝 = 𝑖𝑛𝑠𝑒𝑟𝑡-𝑐ℎ𝑎𝑟
                                                                                                                         
                                                                                                                         𝑐 1 ...𝑐 𝑗 𝑏𝑐 𝑗+1 ...𝑐𝑛 ,
     Computer and Communications Security.                                                                               
[32] Shirin Nilizadeh, Yannic Noller, and Corina S. Pasareanu. 2019. DifFuzz: Differ-
     ential Fuzzing for Side-channel Analysis. In IEEE/ACM International Conference          Tree mutation operations.
     on Software Engineering.                                                                  Given:
[33] Theofilos Petsios, Adrian Tang, Salvatore Stolfo, Angelos D. Keromytis, and
     Suman Jana. 2017. Nezha: Efficient Domain-Independent Differential Testing. In               • 𝑁 is a set of all non-terminal symbols in a CFG.
     IEEE Security & Privacy.                                                                     • 𝑇 is a set of all terminal symbols in a CFG.
[34] Victor Le Pochat, Tom Van Goethem, Samaneh Tajalizadehkhoob, Maciej Ko-
     rczyński, and Wouter Joosen. 2021. Tranco – A Research-Oriented Top Sites                    • 𝐻 is a predefined symbol pool.
     Ranking Hardened Against Manipulation. https://tranco-list.eu/.                         Let 𝑠 be a tree mutable symbol which is represented by a CFG as
[35] PortSwigger. [n.d.]. Exploiting HTTP request smuggling vulnerabilities. https:
     //portswigger.net/web-security/request-smuggling/exploiting.
[36] PortSwigger. 2019. HTTP Request Smuggler. https://github.com/PortSwigger/
                                                                                                                 𝑠 ::= <𝑛 1 >...<𝑛𝑘 > | <𝑛𝑙 >...<𝑛𝑚 > | ...
     http-request-smuggler.
                                                                                             where 𝑠 ∈ 𝑁 , 𝑛𝑖 ∈ 𝑁 for any 1 ≤ 𝑖 ≤ 𝑘 and 𝑙 ≤ 𝑖 ≤ 𝑚. Given the
                                                                                             expansion of 𝑠 → <𝑛 1 >...<𝑛𝑘 >, a tree mutator is represented as a
                                                                                        14
function of the symbol 𝑠, an operation 𝑜𝑝, a sequence position 𝑗,                         <charset-name> ::= "utf-16" | "utf-16BE" | "utf-32" | "utf-32BE" | "us-ascii" |
                                                                                              "iso-8859-1" | "utf-7" | "utf-8"
and a symbol <ℎ>:                                                                        ↩→
                                                                                          <accept-encoding> ::= <accept-encoding-header-name><colon><encodings><newline>
                                                                                          <accept-encoding-header-name> ::= "Accept-Encoding"
                                   <𝑛 1 >...<𝑛 𝑗−1 ><𝑛 𝑗+1 >...<𝑛𝑘 >,                    <encodings> ::= <encoding> | <encoding><comma><encoding>
                                                                                          <encoding> ::= <encoding-name><semicolon><quality>
                                  
                                  
                                  
                                                      if 𝑜𝑝 = 𝑑𝑒𝑙𝑒𝑡𝑒-𝑒𝑙𝑒𝑚                 <encoding-name> ::= "gzip" | "compress" | "deflate" | "br" | "identity" |
                                  
                                  
                                  
                                                                                         ↩→ "chunked"
                                  
                                  
                                   <𝑛 >...<𝑛 ><ℎ><𝑛 >...<𝑛 >,
                                  
                                      1        𝑗−1           𝑗+1       𝑘                 <accept-language> ::= <accept-language-header-name><colon><languages><newline>
𝑔(<𝑛 1 >...<𝑛𝑘 >, 𝑜𝑝, 𝑗, <ℎ>) =                                                           <accept-language-header-name> ::= "Accept-Language"
                                  
                                                     if 𝑜𝑝 = 𝑟𝑒𝑝𝑙𝑎𝑐𝑒-𝑒𝑙𝑒𝑚                <languages> ::= <language> | <language><comma><language>
                                                                                          <language> ::= <language-name><semicolon><quality>
                                  
                                  
                                  
                                  
                                   <𝑛 1 >...<𝑛 𝑗 ><ℎ><𝑛  𝑗+1 >...<𝑛𝑘 >,                  <language-name> ::= "fr" | "en" | "de"
                                                                                          <accept-ranges> ::= <accept-ranges-header-name><colon><range-unit><newline>
                                  
                                  
                                  
                                                     if 𝑜𝑝 = 𝑖𝑛𝑠𝑒𝑟𝑡-𝑒𝑙𝑒𝑚                 <accept-ranges-header-name> ::= "Accept-Ranges"
                                                                                          <range-unit> ::= "bytes" | "none"
where 𝑜𝑝 is the operation type, 1 ≤ 𝑗 ≤ 𝑘, and ℎ ∈ 𝐻 .                                    <allow> ::= <allow-header-name><colon><method-names><newline>
                                                                                          <allow-header-name> ::= "Allow"
                                                                                          <method-names> ::= <method-name> | <method-name><comma><method-name>
                                                                                          <alpn> ::= <alpn-header-name><colon><protocol-ids><newline>
C     FULL GRAMMAR FOR THE REQUEST                                                        <alpn-header-name> ::= "ALPN"
                                                                                          <protocol-ids> ::= <protocol-id> | <protocol-id><comma><protocol-id>
      HEADERS EXPERIMENT                                                                  <protocol-id> ::= "http%2F1.1" | "h2"
                                                                                          <alt-used> ::= <alt-used-header-name><colon><alt-svc><newline>
The request headers experiment tests all standard HTTP request                            <alt-used-header-name> ::= "Alt-Used"
headers together with numerous valid values. Listing 20 shows the                         <alt-svc> : "alternate.example.net"
                                                                                          <authorization> ::=
full grammar where an expansion for every 67 header is defined. In                       ↩→ <authorization-header-name><colon><auth-scheme><space><creds><newline>
addition, we present expansions for sub-elements of each header.                          <authorization-header-name> ::= "Authorization"
                                                                                          <auth-scheme> : "Basic" | "Bearer","Digest","HOBA","Mutual","Negotiate","OAuth",
                                                                                         ↩→ "SCRAM-SHA-1","SCRAM-SHA-256","vapid"
<start> ::= <request>                                                                     <creds> ::= "123456" | "YWxhZGRpbjpvcGVuc2VzYW1l"
<request> ::= <method-name><request-uri><http-version><base><entity-size-header>          <cache-control> ::=
↩→ <some-header><some-header><body>                                                      ↩→ <cache-control-header-name><colon><cache-directives><newline>
 <request-uri> ::= " /_URI_ "                                                             <cache-control-header-name> ::= "Cache-Control"
 <http-version> ::= "HTTP/0.9" | "HTTP/1.0" | "HTTP/1.1"                                  <cache-directives> ::= <cache-directive> |
 <method-name> ::= "GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "CONNECT" |              ↩→ <cache-directive><comma><cache-directive>
↩→ "OPTIONS" | "TRACE"                                                                    <cache-directive> : "max-age=5" | "max-stale=5" | "min-fresh=5" | "no-cache" |
 <base> ::= "\r\nHost: _HOST_\r\nConnection:close\r\nX-Request-ID:                       ↩→ "no-store" | "no-transform" | "only-if-cached"
↩→ _REQUEST_ID_\r\n"                                                                      <caldav-timezones> ::= <caldav-timezones-header-name><colon><boolean><newline>
 <entity-size-header> ::= <content-length> | <chunked-transfer-encoding> |                <caldav-timezones-header-name> ::= "CalDav-Timezones"
↩→ <content-length><chunked-transfer-encoding> |                                          <cdn-loop> ::= <cdn-loop-header-name><colon><cdn-infos><newline>
↩→ <chunked-transfer-encoding><content-length>                                            <cdn-loop-header-name> ::= "CDN-Loop"
 <some-header> ::= <accept> | <accept-charset> | <accept-encoding> |                      <cdn-infos> ::= <cdn-info> | <cdn-info><comma><cdn-info>
↩→ <accept-language> | <accept-ranges> | <allow> | <alpn> | <alt-used> |                  <cdn-info> : "foo123.foocdn.example" | "barcdn.example; trace='abcdef'" |
↩→ <authorization> | <cache-control> | <caldav-timezones> | <cdn-loop> |                 ↩→ "AnotherCDN; abc=123; def='456'"
↩→ <content-encoding> | <content-language> | <content-length> |                           <content-encoding> ::=
↩→ <content-location> | <cookie> | <date> | <depth> | <destination> |
                                                                                         ↩→ <content-encoding-header-name><colon><transfer-encodings><newline>
↩→ <early-data> | <expect> | <expires> | <forwarded> | <from> | <http2-settings>
                                                                                          <content-encoding-header-name> ::= "Content-Encoding"
↩→ | <if> | <if-match> | <if-modified-since> | <if-none-match> | <if-range> |
                                                                                          <content-language> ::= <content-language-header-name><colon><languages><newline>
↩→ <if-schedule-tag-match> | <if-unmodified-since> | <link> | <max-forwards> |
                                                                                          <content-language-header-name> ::= "Content-Language"
↩→ <mime-version> | <odata-isolation> | <odata-maxversion> | <odata-version> |
                                                                                          <content-length> ::=
↩→ <ordering-type> | <origin> | <oscore> | <overwrite> | <position> | <pragma> |
                                                                                         ↩→ <content-length-header-name><colon><content-length-value><newline>
↩→ <prefer> | <proxy-authorization> | <range> | <referer> | <schedule-reply> |
↩→ <sec-token-binding> | <sec-websocket-accept> | <sec-websocket-extensions> |
                                                                                          <content-length-header-name> ::= "Content-Length"
↩→ <sec-websocket-key> | <sec-websocket-protocol> | <sec-websocket-version> |
                                                                                          <content-length-value> ::= "40" | "60" | "80"
↩→ <slug> | <te> | <timeout> | <topic> | <trailer> | <transfer-encoding> | <ttl>
                                                                                          <content-location> ::=
↩→ | <urgency> | <upgrade> | <user-agent> | <via>                                        ↩→ <content-location-header-name><colon><content-location-value><newline>
 <newline> ::= "\r\n"                                                                     <content-location-header-name> ::= "Content-Location"
 <body> ::=                                                                               <content-location-value> ::= <absolute-uri> | <relative-uri>
↩→ "\r\nA\r\nBBBBBBBBBB\r\n0\r\n\r\nBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB               <absolute-uri> : "http://example.com/example"
↩→ BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"                         <relative-uri> ::= "/example"
 <comma> ::= ","                                                                          <cookie> ::= <cookie-header-name><colon><cookie-value><newline>
 <colon> ::= ":"                                                                          <cookie-header-name> ::= "Cookie"
 <semicolon> ::= ";"                                                                      <cookie-value> : "SID=31d4d96e407aad42" | "PHPSESSID=298zf09hf012fh2;
 <space> ::= " "                                                                         ↩→ csrftoken=u32t4o3tb3gg43; _gat=1"
 <start-tag> ::= "<"                                                                      <date> ::= <date-header-name><colon><date-value><newline>
 <end-tag> ::= ">"                                                                        <date-header-name> ::= "Date"
 <start-parenthesis> ::= "("                                                              <date-value> : "Sun, 06 Nov 1994 08:49:37 GMT" | "Sun, 06 Nov 2094 08:49:37 GMT"
 <end-parenthesis> ::= ")"                                                                <depth> ::= <depth-header-name><colon><depth-value><newline>
 <equals> ::= "="                                                                         <depth-header-name> ::= "Depth"
 <boolean> ::= "T" | "F"                                                                  <depth-value> : "0" | "1" | "infinity"
 <quality> ::= "q=1.0" | "q=0.0"                                                          <destination> ::= <destination-header-name><colon><absolute-uri><newline>
 <chunked-transfer-encoding> ::=                                                          <destination-header-name> ::= "Destination"
↩→ <transfer-encoding-header-name><colon><chunked-encoding><newline>                      <early-data> ::= <early-data-header-name><colon><early-data-value><newline>
 <chunked-encoding> : "chunked"                                                           <early-data-header-name> ::= "Early-Data"
 <accept> ::= <accept-header-name><colon><accept-types><newline>                          <early-data-value> : "1"
 <accept-header-name> ::= "Accept"                                                        <expect> ::= <expect-header-name><colon><expect-value><newline>
 <accept-types> ::= <accept-type> | <accept-type><comma><accept-type>                     <expect-header-name> ::= "Expect"
 <accept-type> ::= <mime-type-subtype><semicolon><quality>                                <expect-value> : "100-continue"
 <mime-type-subtype> ::= "*/*" | "application/octet-stream" | "application/pdf" |         <expires> ::= <expires-header-name><colon><date-value><newline>
↩→ "application/pkcs8" | "application/zip" | "audio/mpeg" | "audio/vorbis" |              <expires-header-name> ::= "Expires"
↩→ "audio/example" | "font/woff" | "font/ttf" | "font/otf" | "image/jpeg" |               <forwarded> ::= <forwarded-header-name><colon><by><space><absolute-uri><newline>
↩→ "image/png" | "image/svg+xml" | "model/3mf" | "text/html" | "video/mp4"                <forwarded-header-name> ::= "Forwarded"
 <accept-charset> ::= <accept-charset-header-name><colon><charsets><newline>              <by> ::= "by"
 <accept-charset-header-name> ::= "Accept-Charset"                                        <from> ::= <from-header-name><colon><mailbox><newline>
 <charsets> ::= <charset> | <charset><comma><charset>                                     <from-header-name> ::= "From"
 <charset> ::= <charset-name><semicolon><quality>                                         <mailbox> ::= "webmaster@w3.org"

                                                                                    15
 <http2-settings> ::= <http2-settings-header-name><colon><setting><newline>              <sec-websocket-accept> ::=
 <http2-settings-header-name> ::= "HTTP2-Settings"                                       ↩→ <sec-websocket-accept-header-name><colon><sec-websocket-accept-value><newline>
 <setting> ::= "AAMAAABkAARAAAAAAAIAAAAA"                                                 <sec-websocket-accept-header-name> ::= "Sec-Websocket-Accept"
 <if> ::= <if-header-name><colon><tag-list><newline>                                      <sec-websocket-accept-value> ::= "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="
 <if-header-name> ::= "If"                                                                <sec-websocket-extensions> ::= <sec-websocket-extensions-header-name><colon>
 <urn-tag> ::= <start-tag><urn-value><end-tag>                                           ↩→ <sec-websocket-extensions-values><newline>
 <urn-value> ::= "urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2" |                        <sec-websocket-extensions-header-name> ::= "Sec-Websocket-Extensions"
↩→ "urn:uuid:58f202ac-22cf-11d1-b12d-002035b29092"                                        <sec-websocket-extensions-values> ::= <sec-websocket-extensions-value> |
 <tag-list> : <start-parenthesis><urn-tag><end-parenthesis>                              ↩→ <sec-websocket-extensions-value><comma><sec-websocket-extensions-value>
 <if-match> ::= <if-match-header-name><colon><entity-tags><newline>                       <sec-websocket-extensions-value> ::= "deflate-stream" | "mux" | "max-channels:4;
 <if-match-header-name> ::= "If-Match"
                                                                                         ↩→ flow-control"
 <entity-tags> ::= <entity-tag> | <entity-tag><comma><entity-tag>
                                                                                          <sec-websocket-key> ::=
 <entity-tag> : "*" | "'xyzzy'"
                                                                                         ↩→ <sec-websocket-key-header-name><colon><sec-websocket-key-value><newline>
 <if-modified-since> ::=
                                                                                          <sec-websocket-key-header-name> ::= "Sec-Websocket-Key"
↩→ <if-modified-since-header-name><colon><date-value><newline>
                                                                                          <sec-websocket-key-value> ::= "dGhlIHNhbXBsZSBub25jZQ=="
 <if-modified-since-header-name> ::= "If-Modified-Since"
                                                                                          <sec-websocket-protocol> ::=
 <if-none-match> ::= <if-none-match-header-name><colon><entity-tags><newline>
                                                                                         ↩→ <sec-websocket-protocol-header-name><colon><sec-websocket-protocol-values><newline>
 <if-none-match-header-name> ::= "If-None-Match"
                                                                                          <sec-websocket-protocol-header-name> ::= "Sec-Websocket-Protocol"
 <if-range> ::= <if-range-header-name><colon><if-range-value><newline>
                                                                                          <sec-websocket-protocol-values> ::= <sec-websocket-protocol-value> |
 <if-range-header-name> ::= "If-Range"
 <if-range-value> ::= <entity-tag> | <date-value>                                        ↩→ <sec-websocket-protocol-value><comma><sec-websocket-protocol-value>
 <if-schedule-tag-match> ::=                                                              <sec-websocket-protocol-value> ::= "chat" | "superchat"
↩→ <if-schedule-tag-match-header-name><colon><entity-tag><newline>
                                                                                          <sec-websocket-version> ::=
 <if-schedule-tag-match-header-name> ::= "If-Schedule-Tag-Match"                         ↩→ <sec-websocket-version-header-name><colon><sec-websocket-version-value><newline>
 <if-unmodified-since> ::=                                                                <sec-websocket-version-header-name> ::= "Sec-Websocket-Version"
↩→ <if-unmodified-since-header-name><colon><date-value><newline>
                                                                                          <sec-websocket-version-value> ::= "13"
 <if-unmodified-since-header-name> ::= "If-Unmodified-Since"                              <slug> ::= <slug-header-name><colon><slug-value><newline>
 <link> ::= <link-header-name><colon><link-value><newline>                                <slug-header-name> ::= "Slug"
                                                                                          <slug-value> ::= "The Beach at S%C3%A8te"
 <link-header-name> ::= "Link"
                                                                                          <te> ::= <te-header-name><colon><te-encodings><newline>
 <link-value> ::= <start-tag><absolute-uri><end-tag>
 <max-forwards> ::=                                                                       <te-header-name> ::= "TE"
                                                                                          <te-encodings> ::= <te-encoding> | <te-encoding><comma><te-encoding>
↩→ <max-forwards-header-name><colon><max-forwards-value><newline>
                                                                                          <te-encoding> ::= <te-encoding-name><semicolon><quality>
 <max-forwards-header-name> ::= "Max-Forwards"
                                                                                          <te-encoding-name> ::= "gzip" | "compress" | "deflate" | "br" | "identity" |
 <max-forwards-value> : "0" | "1"
                                                                                         ↩→ "chunked" | "trailers"
 <mime-version> ::=
                                                                                          <timeout> ::= <timeout-header-name><colon><timeout-values><newline>
↩→ <mime-version-header-name><colon><mime-version-value><newline>
                                                                                          <timeout-header-name> ::= "Timeout"
 <mime-version-header-name> ::= "MIME-Version"
                                                                                          <timeout-values> ::= <timeout-value> | <timeout-value><comma><timeout-value>
 <mime-version-value> : "1.0" | "1.1"
                                                                                          <timeout-value> ::= "Infinite" | "Second-4100000000"
 <odata-isolation> ::=
                                                                                          <topic> ::= <topic-header-name><colon><topic-value><newline>
↩→ <odata-isolation-header-name><colon><odata-isolation-value><newline>                   <topic-header-name> ::= "Topic"
 <odata-isolation-header-name> ::= "OData-Isolation"                                      <topic-value> ::= "upd"
 <odata-isolation-value> : "snapshot"                                                     <trailer> ::= <trailer-header-name><colon><trailer-value><newline>
 <odata-maxversion> ::=                                                                   <trailer-header-name> ::= "Trailer"
↩→ <odata-maxversion-header-name><colon><odata-version-value><newline>                    <trailer-value> ::= "Expires"
 <odata-maxversion-header-name> ::= "OData-MaxVersion"                                    <transfer-encoding> ::=
 <odata-version-value> : "4.0"                                                           ↩→ <transfer-encoding-header-name><colon><transfer-encodings><newline>
 <odata-version> ::=                                                                      <transfer-encoding-header-name> ::= "Transfer-Encoding"
↩→ <odata-version-header-name><colon><odata-version-value><newline>                       <transfer-encodings> ::= <encoding-name> | <encoding-name><comma><encoding-name>
 <odata-version-header-name> ::= "OData-Version"                                          <ttl> ::= <ttl-header-name><colon><ttl-value><newline>
 <ordering-type> ::=                                                                      <ttl-header-name> ::= "TTL"
↩→ <ordering-type-header-name><colon><ordering-type-value><newline>                       <ttl-value> ::= "0" | "1"
 <ordering-type-header-name> ::= "Ordering-Type"                                          <urgency> ::= <urgency-header-name><colon><urgency-value><newline>
 <ordering-type-value> ::= "DAV:unordered" | "DAV:custom" |                               <urgency-header-name> ::= "Urgency"
↩→ "http://example.org/example.html"                                                      <urgency-value> ::= "very-low" | "low" | "normal" | "high"
 <origin> ::= <origin-header-name><colon><origin-value><newline>                          <upgrade> ::= <upgrade-header-name><colon><upgrade-values><newline>
 <origin-header-name> ::= "Origin"                                                        <upgrade-header-name> ::= "Upgrade"
 <origin-value> ::= "http://example.com" | "null"                                         <upgrade-values> ::= <upgrade-value> | <upgrade-value><comma><upgrade-value>
 <oscore> ::= <oscore-header-name><colon><oscore-value><newline>                          <upgrade-value> ::= "websocket" | "HTTP/2.0" | "SHTTP/1.3" | "IRC/6.9" |
 <oscore-header-name> ::= "OSCORE"                                                       ↩→ "RTA/x11"
 <oscore-value> ::= "CSU" | "AA"                                                          <user-agent> ::= <user-agent-header-name><colon><user-agent-value><newline>
 <overwrite> ::= <overwrite-header-name><colon><boolean><newline>                         <user-agent-header-name> ::= "User-Agent"
 <overwrite-header-name> ::= "Overwrite"                                                  <user-agent-value> ::= "curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3"
 <position> ::= <position-header-name><colon><position-value><newline>                    <via> ::= <via-header-name><colon><via-values><newline>
 <position-header-name> ::= "Position"                                                    <via-header-name> ::= "Via"
 <position-value> : "first" | "last" | "after example.html"                               <via-values> ::= <via-value> | <via-value><comma><via-value>
 <pragma> ::= <pragma-header-name><colon><pragma-directive><newline>                      <via-value> ::= "1.0 fred" | "1.1 p.example.net"
 <pragma-header-name> ::= "Pragma"
 <pragma-directive> : "no-cache"
 <prefer> ::= <prefer-header-name><colon><preferences><newline>
                                                                                          Listing 20: The full grammar for the request headers experiment.
 <prefer-header-name> ::= "Prefer"
 <preferences> ::= <preference> | <preference><comma><preference>
 <preference> : "respond-async" | "wait=100" | "handling=lenient"
 <proxy-authorization> ::=
↩→ <proxy-authorization-header-name><colon><auth-scheme><space><creds><newline>
 <proxy-authorization-header-name> ::= "Proxy-Authorization"
 <range> ::= <range-header-name><colon><range-unit><equals><range-value><newline>
 <range-header-name> ::= "Range"
 <range-value> ::= "5-8" | "5-"
 <referer> ::= <referer-header-name><colon><absolute-uri><newline>
 <referer-header-name> ::= "Referer"
 <schedule-reply> ::= <schedule-reply-header-name><colon><boolean><newline>
 <schedule-reply-header-name> ::= "Schedule-Reply"
 <sec-token-binding> ::=
↩→ <sec-token-binding-header-name><colon><sec-token-binding-value><newline>
 <sec-token-binding-header-name> ::= "Sec-Token-Binding"
 <sec-token-binding-value> ::= "AIkAAgBBQLgtRpWFPN66kxhxGrtaKrzcMtHw7HV8"


                                                                                    16
