# HTTP请求走私

在本节中，我们将讲解HTTP请求走私攻击，并描述常见的请求走私漏洞是如何产生的。

![](https://2165487371-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FfahSMMYUzmqmE8SfCaax%2Fuploads%2Fgit-blob-457891b76f8a16f0f577c3abe9dccd2326ab53e0%2Fhttp-request-smuggling.svg?alt=media)

> **Labs**
>
> 如果你已经熟悉HTTP请求走私，只想在一系列故意易受攻击的网站上进行练习，请点击下面的链接查看本专题中所有实验的概述。
>
> [查看所有HTTP请求走私实验](https://portswigger.net/web-security/all-labs#http-request-smuggling)

## 什么是HTTP请求走私？

HTTP请求走私是一种干扰网站处理一个或多个用户发送的HTTP请 求序列的技术。请求走私漏洞在本质上通常是严重的，它允许攻击者绕过安全控制，未经授权访问敏感数据，并直接危及其他应用程序用户。

> **注意**
>
> HTTP请求走私首次在2005年被记录，最近因[PortSwigger的研究](https://portswigger.net/blog/http-desync-attacks-request-smuggling-reborn)而重新流行。

## HTTP请求走私攻击中发生了什么？

今天的Web应用程序经常在用户和最终应用程序逻辑之间使用HTTP服务器链。用户向前端服务器（有时称为负载均衡或反向代理）发送请求，此服务器将请求转发给一个或多个后端服务器。这种类型的架构在现代基于云的应用程序中越来越普遍，而且在某些情况下是不可避免的。

当前端服务器将HTTP请求转发到后端服务器时，它通常会在同一后端网络连接上发送多个请求，因为这样做更有效率且性能更高。协议非常简单：一个接一个地发送HTTP请求，接收服务器解析HTTP请求标头以确定一个请求的结束和下一个请求的开始：

![](https://2165487371-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FfahSMMYUzmqmE8SfCaax%2Fuploads%2Fgit-blob-85eec4c9f04d41f65d1d6c7e7d291a1db6dadeb1%2Fforwarding-http-requests-to-back-end-server.svg?alt=media)

在这种情况下，前端和后端系统对请求之间的界限达成一致是非常关键的。否则，攻击者可能会发送一个模糊的请求，该请求会被前端和后端系统作出不同的解释：

![](https://2165487371-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FfahSMMYUzmqmE8SfCaax%2Fuploads%2Fgit-blob-ff0831b1ec798353669d2c85fea111ef1318f99d%2Fsmuggling-http-request-to-back-end-server.svg?alt=media)

在这里，攻击者使得其前端请求的一部分被后端服务器解释为下一个请求的开始。它实际上被附加到下一个请求的开头，因此可以干扰应用程序处理该请求的方式。这就是请求走私攻击，它可能会产生毁灭性的结果。

## HTTP请求走私漏洞是如何产生的？

大多数HTTP请求走私漏洞产生的原因是，HTTP规范提供了两种不同的方法来指定请求结束的位置：`Content-Length`标头和`Transfer-Encoding`标头。

`Content-Length`标头很简单：它指定消息正文的长度（以字节为单位）。例如：

```http
POST /search HTTP/1.1
Host: normal-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 11

q=smuggling
```

`Transfer-Encoding`标头可用于指定消息正文使用分块编码。这意味着消息正文包含一个或多个数据块。每个分块由以字节为单位的分块大小（用十六进制表示）组成，然后是分块内容。消息以一个大小为零的块结束。例如：

```http
POST /search HTTP/1.1
Host: normal-website.com
Content-Type: application/x-www-form-urlencoded
Transfer-Encoding: chunked

b
q=smuggling
0
```

> **注意**
>
> 许多安全测试人员并不知道分块编码可以用于HTTP请求，原因有两个：
>
> * Burp Suite会自动解包分块编码，使消息更容易查看和编辑。
> * 浏览器通常不会在请求中使用分块编码，分块编码通常只在服务器响应中出现。

由于HTTP规范提供了两种不同的方法来指定HTTP消息的长度，因此一个消息可以同时使用这两种方法，从而使它们相互冲突。HTTP规范试图通过声明，如果`Content-Length`和`Transfer-Encoding`标头同时存在，则应忽略`Content-Length`标头来解决这个问题。当只有一个服务器时，这可能足以避免歧义，但在涉及两个或更多服务器时可能就不行了。在这种情况下，问题可能出现的原因有两个：

* 有些服务器不支持请求中的`Transfer-Encoding`标头。
* 如果标头以某种方式被混淆，有些支持`Transfer-Encoding`标头的服务器就会被诱使而不处理它。

如果前端和后端服务器对（可能混淆的）`Transfer-Encoding`标头的处理方式不同，那么它们可能对连续请求之间的界限产生分歧，从而导致请求走私漏洞。

## 如何进行HTTP请求走私攻击

请求走私攻击涉及将`Content-Length`标头和`Transfer-Encoding`标头放入单个HTTP请求中，并对其进行操作，使前端和后端服务器对请求进行不同的处理。具体操作方式取决于这两个服务器的行为：

* CL.TE：前端服务器使用`Content-Length`标头，后端服务器使用`Transfer-Encoding`标头。
* TE.CL：前端服务器使用`Transfer-Encoding`标头，后端服务器使用`Content-Length`标头。
* TE.TE：前端和后端服务器都支持`Transfer-Encoding`标头，但可以通过某种方式混淆头部来诱导其中一个服务器不处理它。

> **注意**
>
> 这些技巧仅适用于HTTP/1请求。浏览器和其他客户端（包括Burp）默认使用HTTP/2与服务器进行通信，这些服务器通过ALPN作为TLS握手的一部分明确地宣告支持HTTP/2。因此，在测试支持HTTP/2的站点时，你需要在Burp Repeater中手动切换协议。你可以在**Inspector**面板的**Request attributes**部分执行此操作。

### **CL.TE漏洞**

在这里，前端服务器使用`Content-Length`标头，后端服务器使用`Transfer-Encoding`标头。我们可以执行一个简单的HTTP请求走私攻击，如下所示：

```
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 13
Transfer-Encoding: chunked

0

SMUGGLED
```

前端服务器处理`Content-Length`标头，并确定请求体的长度为13字节，直到`SMUGGLED`的结尾。此请求将转发给后端服务器。

后端服务器处理`Transfer-Encoding`标头，将消息正文视为使用分块编码。它处理第一个分块，该分块的长度声明为零，因此被视为终止请求。随后的`SMUGGLED`字节未被处理，后端服务器将把这些字节视为序列中下一个请求的开始。

> **LAB**
>
> [HTTP请求走私，基础的CL.TE漏洞](https://portswigger.net/web-security/request-smuggling/lab-basic-cl-te)

### **TE.CL漏洞**

在这里，前端服务器使用`Transfer-Encoding`标头，后端服务器使用`Content-Length`标头。我们可以执行一个简单的HTTP请求走私攻击，如下所示：

```
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 3
Transfer-Encoding: chunked

8
SMUGGLED
0
```

> **注意**
>
> 要使用Burp Repeater发送此请求，你首先需要转到Repeater菜单并确保“Update Content-Length”选项未选中。
>
> 在最后的`0`之后，需要包含尾随序列`\r\n\r`。

前端服务器处理`Transfer-Encoding`标头，将消息正文视为使用分块编码。它处理第一个分块，声明长度为8个字节，直到`SMUGGLED`之后的行的开头。它处理第二个分块，该分块的长度为零，因此被视为终止请求。此请求将转发给后端服务器。

后端服务器处理`Content-Length`标头，并确定请求体长度为3个字节，直到`8`之后的行的开头。随后的字节，以`SMUGGLED`开头，未被处理，后端服务器将把这些字节视为序列中下一个请求的开始。

> **LAB**
>
> [HTTP请求走私，基础的TE.CL漏洞](https://portswigger.net/web-security/request-smuggling/lab-basic-te-cl)

### **TE.TE行为：混淆TE头**

在这里，前端和后端服务器都支持`Transfer-Encoding`标头，但可以通过某种方式混淆头部来诱导其中一个服务器不处理它。

要混淆`Transfer-Encoding`标头的方法有很多。例如：

```
Transfer-Encoding: xchunked

Transfer-Encoding : chunked

Transfer-Encoding: chunked
Transfer-Encoding: x

Transfer-Encoding:[tab]chunked

[space]Transfer-Encoding: chunked

X: X[\n]Transfer-Encoding: chunked

Transfer-Encoding
: chunked
```

这些技巧中的每一种都涉及到对HTTP规范的微妙偏离。实现协议规范的实际代码很少完全精确地遵循它，而且不同实现对规范的不同变体的容忍度也有所不同。为了发现TE.TE漏洞，需要找到`Transfer-Encoding`标头的某种变体，使得前端或后端服务器中只有一个处理它，而另一个服务器忽略它。

取决于是前端还是后端服务器可以被诱导不处理混淆的`Transfer-Encoding`标头，攻击的其余部分，将采取与已经描述的CL.TE或TE.CL漏洞相同的形式。

> **LAB**
>
> [HTTP请求走私，混淆TE头](https://portswigger.net/web-security/request-smuggling/lab-obfuscating-te-header)

## 如何识别HTTP请求走私漏洞

查看以下部分，了解如何自行识别HTTP请求走私漏洞的一些建议。我们还提供了一些交互式实验，以便你了解实际操作中的情况。

> **阅读更多**
>
> [查找HTTP请求走私漏洞](https://portswigger.net/web-security/request-smuggling/finding)

## 如何利用HTTP请求走私漏洞

现在你已经熟悉了基本概念，让我们看看如何利用HTTP请求走私来实施一些高危攻击。与往常一样，我们提供了很多完全交互式的实验，让你尝试攻击现实目标。

> **阅读更多**
>
> [利用HTTP请求走私漏洞](https://portswigger.net/web-security/request-smuggling/exploiting)

## 高级HTTP请求走私

如果已经完成了我们的其他请求走私实验，那么你可以学习一些更高级的技巧了。我们根据PortSwigger研究人员发现的真实世界的漏洞，创建了一些交互式的实验。你甚至有机会尝试使用Burp的独特功能进行基于HTTP/2的测试。

> **阅读更多**
>
> [高级HTTP请求走私漏洞](https://portswigger.net/web-security/request-smuggling/advanced)

## 浏览器驱动的请求走私

迄今为止，你所学到的请求走私技术依赖于使用专用的黑客工具（如Burp Repeater）发送有意畸形的请求。事实上，你可以使用完全兼容浏览器的请求来执行相同的攻击，使用完全正常的`Content-Length`标头来使两个服务器不同步。

这甚至使你能够发起这些攻击的客户端变种，诱使受害者的浏览器毒化自己与易受攻击网站的连接。这不仅使单服务器站点受到请求走私风格的攻击，甚至使你能够攻击那些无法直接访问的站点。请查看学习材料和实验，了解如何操作。

> **阅读更多**
>
> [浏览器驱动的请求走私](https://portswigger.net/web-security/request-smuggling/browser)

## 如何防止HTTP请求走私漏洞

HTTP请求走私漏洞出现在，前端服务器和后端服务器使用不同机制确定请求之间的边界的情况下。这可能是由于HTTP/1服务器是使用`Content-Length`标头还是分块传输编码来确定每个请求的结束位置之间存在差异。在 HTTP/2 环境中，为后端降级HTTP/2请求的常见做法也充满了问题，导致或简化了许多额外的攻击。

为了防止HTTP请求走私漏洞，我们建议采取以下高级措施：

* 尽可能端到端使用HTTP/2，并禁用HTTP降级。HTTP/2使用一种健壮的机制来确定请求的长度，端到端使用时，本身具有防止请求走私的能力。如果无法避免HTTP降级，请确保根据HTTP/1.1规范验证重写的请求。例如，拒绝那些在标头中含有换行符、在标头名称中含有冒号、在请求方法中含有空格的请求。
* 使前端服务器将模糊的请求规范化，并使后端服务器拒绝任何仍然模糊的请求，在此处理过程中关闭TCP连接。
* 永远不要假设请求不会有正文。这是CL.0和客户端解异步漏洞的根本原因。
* 如果在处理请求时触发了服务器级异常，那么默认丢弃连接。
* 如果你通过转发代理路由流量，如果可能的话，请确保启用上游的HTTP/2。

正如我们在学习材料中所展示的，禁用后端连接重用将有助于缓解某些类型的攻击，但这仍无法保护你免受请求隧道攻击的侵害。

> **阅读更多**
>
> [使用Burp Suite的Web漏洞扫描器发现HTTP请求走私漏洞](https://portswigger.net/burp/vulnerability-scanner)
