What is HTTP Request Smuggling?
TTP request smuggling is a vulnerability that arises from inconsistencies within HTTP request parsing between multiple devices. These vulnerabilities occur when a front-end device (e.g., CDN, load balancer, WAF), and a back-end device (e.g., web-server) interpret the end of the HTTP request differently, due to the presence of different HTTP headers (i.e., Transfer-Encoding, Content-Length
). This can lead to a bypass of security controls, an ability to launch attacks (e.g., XSS) on other users, cache poisoning, denial of service, or other attacks depending on the application. HTTP request smuggling may also be referred to as CWE-444, HTTP response smuggling, or HTTP smuggling.
There are many types of HTTP request smuggling vulnerabilities, depending on the protocol version, header ordering, and environment. We’ll break down each of these in the following sections.
What we will cover:
What are the different types of HTTP request smuggling?
HTTP/2 request smuggling
Impacts of HTTP request smuggling
How to prevent HTTP request smuggling
Summary
What are the different types of HTTP request smuggling?
HTTP/1.1 request smuggling
HTTP requests using version 1.1 can specify the end of their request using either the Content-Length or Transfer-Encoding
headers. When front-end and back-end devices do not use the same of these two headers to determine the end of the request, they may parse the end of the request differently, leading to an HTTP request smuggling vulnerability.
For the following examples, we’ll demonstrate the impact of request smuggling through an authorization bypass. In this scenario, the front-end server prevents access to the /admin page, but there is no additional validation on the back-end. Since the back-end does not also perform validation, request smuggling can allow us to bypass this authorization check.
CL.TE vulnerabilities
A CL.TE vulnerability refers to an HTTP request smuggling attack where the front-end uses the Content-Length
header, but the back-end uses the Transfer-Encoding
header.
In the following example, the front-end uses the Content-Length
header, reading the entire contents as a single request and forwarding it to the back-end. However, the back-end server uses the Transfer-Encoding
header and interprets the end of the request at the 0\r\n\r\n
(two new lines after a 0 chunk which Transfer-Encoding uses to denote the end of a request). The back-end thus interprets this as two requests, with the first highlighted in red, and the second in blue.
POST /smuggled HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 46
Transfer-Encoding: chunked
1
q=smuggling
0
GET /admin HTTP/1.1
Example: x
Now, when more data comes through the connection (e.g., we send the request again), the back-end server prepends it to this smuggled second request because it was not terminated. This causes the back-end server to see the following as the second request:
GET /admin HTTP/1.1
Example: xPOST /smuggled HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 46
Transfer-Encoding: chunked
1
q=smuggling
0
The back-end server interprets this as a GET request for /admin, which was smuggled past the front-end server, which only interpreted the two POST requests. This request smuggling vulnerability now permits the attacker to bypass the authorization check by the front-end.
TE.CL vulnerabilities
A TE.CL vulnerability refers to an HTTP request smuggling attack where the front-end uses the Transfer-Encoding header but the back-end uses the Content-Length header.
In the following example, the front-end uses the Transfer-Encoding
header, reading the entire contents as a single request and forwards it to the back-end. The back-end uses the Content-Length
header, and thus interprets this as two requests, with the first highlighted in red, and the second in blue.
POST /smuggled HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 4
Transfer-Encoding: chunked
k0
GET /admin HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 150
x
0
While the front-end server has finished its one request, the back-end server is still awaiting additional content from the long Content-Length
in the smuggled request. When more data comes through the connection (e.g., we send the request again), the back-end server prepends it to this smuggled second request because it was not terminated. This causes the back-end server to see the following as the second request:GET /admin HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 141
x
0
POST /smuggled HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 4
Transfer-Encoding: chunked
k0
The back-end server interprets this as a GET request for /admin, which was smuggled past the front-end server, which only interpreted the two POST requests. This request smuggling vulnerability now permits the attacker to bypass the authorization check by the front-end.
Header obfuscation & smuggling
It is possible that some devices will ignore Transfer-Encoding
headers that are malformed, whereas others may process them even with slight errors (e.g., extra spaces, duplicate headers, etc.) If this is the case, it may make it easier to exploit one of the aforementioned CL.TE or TE.CL vulnerabilities because one device is processing the malformed header while the other ignores it.
HTTP/2 request smuggling
While classic HTTP request smuggling should be mitigated via HTTP/2, there are still ways to smuggle requests using HTTP/2, particularly when backend servers only support HTTP/1.1.
HTTP/2 is a binary protocol sent in frames, with each frame being preceded by the frame’s length. A Content-Length
header is not required, and the Transfer-Encoding
header is not supported. We’ll display these requests as they would be in HTTP/1.1 for readability, but keep in mind that the actual data sent looks different in HTTP/2.
HTTP/2 Downgrading
When back-end devices (e.g., web servers) only support HTTP/1.1, but front-end devices (e.g., CDNs) and clients are using HTTP/2, front-end devices will rewrite their received HTTP/2 request to HTTP/1.1 before sending it to the back-end. This is referred to as HTTP/2 downgrading, and exploiting errors in the rewrite is how request smuggling vulnerabilities arise.
H2.CL vulnerabilities
HTTP/2 does not require a Content-Length
header, and if one is provided, it is supposed to be validated against the calculated length of the message. When a device is rewriting a request to HTTP/1.1, it should use its calculated length for the translated Content-Length
header. However, some front-end devices may instead use the provided Content-Length
header when performing the downgrade. If this happens, the back-end will use a different Content-Length
than the front-end, resulting in a smuggling vulnerability.
H2.TE vulnerabilities
The Transfer-Encoding
header is not used in HTTP/2, and it is recommended to either strip the header or reject HTTP/2 requests that have a Transfer-Encoding
header. If the front-end device does not do this, and then rewrites the request with the Transfer-Encoding
header, it is possible to smuggle the header to the back-end. Exploiting this would require the back-end to support Transfer-Encoding
over Content-Length
, but would then be the same as a CL.TE HTTP/1.1 exploitation.
Impacts of HTTP request smuggling
HTTP request smuggling has wide ranging impacts depending on the application because it interferes with the boundary of multiple requests. Our earlier examples covered an authorization bypass and we’ll cover an additional one that impacts other users, but there are many more covered in further detail by the folks at Portswigger, who perform extensive HTTP request smuggling research.
Real-life example: Exploiting with reflected XSS
In this example, let’s assume the front-end server determines request length based on Content-Length
, and the back-end server determines it based on Transfer-Encoding
. Let’s also pretend the web server is vulnerable to reflected Cross-Site Scripting (XSS) when a payload is in the Referrer header. Normally, a reflected XSS in the Referer header is hard to exploit, because it cannot be exploited with just a link the way it could be in a query parameter. In this scenario, an attacker sends the following request:
POST /smuggled HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 111
Transfer-Encoding: chunked
1
q=smuggling
0
GET /vulnerable-page HTTP/1.1
Referrer: ”><script>print(1)</script>
Example: x
The front-end uses the Content-Length
header and interprets this as a single request, forwarding it to the back-end. The back-end uses Transfer-Encoding
, and interprets this as two requests, with the first highlighted in red, and the second in blue. The second request is incomplete, so the back-end waits for more data. When another user sends in a request to /home after this, the web server’s response is from the attacker’s smuggled request, leading to the delivery of the reflected XSS payload in the response to the victim user. In this scenario, the back-end interprets the victim user’s request like the following:
GET /vulnerable-page HTTP/1.1
Referrer: "><script>print(1)</script>
Example: xGET /home HTTP/1.1
Host: example.com
...rest of victim user's request...
The victim user sent a request to /home, but instead received the response to /vulnerable-page with the reflected XSS payload. In practice, due to request volume, connection re-use, etc. it will take many requests to cause a user to receive the queued XSS response, but this can quickly affect a wide range of users by rapidly sending smuggling requests to poison connections with XSS payloads.
How to prevent HTTP request smuggling
Use HTTP/2 end to end, and disable downgrading
HTTP/2 is inherently protected against request smuggling when downgrading is disabled. When possible, it is the best mitigation.
Reject malformed requests (both HTTP/2 and HTTP/1.1)
Rejecting requests with malformed headers, duplicate headers, etc. will help prevent vulnerabilities that may rely on servers accepting malformed headers. It will also prevent HTTP/2 downgrade CL and TE vulnerabilities that rely on additional unneeded headers being forwarded to the back-end.
Normalize ambiguous requests
Front-end servers receiving both CL and TE headers should normalize requests to a single mechanism for determining the length of a request, and remove the extraneous headers to prevent origins from receiving ambiguous requests.
Validate on HTTP/1.1 downgrade
Front-end servers downgrading to HTTP/1.1 should validate that rewritten requests are not malformed or ambiguous. For example, after rewriting, a front-end server should perform the same normalization that it would on a regular HTTP/1.1 request.
Consider disabling re-use of back-end connections
Many request smuggling attacks make use of the front-end and back-end re-using connections. The XSS example does this, where multiple users sending requests share a front-end to back-end connection and smuggling causes responses to get mixed. Disabling connection re-use prevents this kind of attack, but does not prevent all smuggling attacks, and does not prevent advanced techniques such as request tunneling. This may also impact performance, and is only a partial mitigation.
Summary
HTTP Request smuggling is a vulnerability that arises from inconsistencies within HTTP request parsing between multiple devices and can cause devastating impacts such as authorization bypass, an ability to launch attacks (e.g., XSS) on other users, cache poisoning, denial of service, and more. While HTTP/2 and disabling downgrading will prevent request smuggling, organizations should also consider rejecting malformed requests, normalizing ambiguous requests, and validating HTTP schema to further limit the attack surface if downgrading must be supported.
Learn more about Fastly’s security offerings that can help keep you safe from HTTP attacks and more.
Learn more about Fastly’s security offerings