HTTP/1.1 在安全层面已经“走到尽头”

HTTP/1.1 在安全层面已经“走到尽头”

内容基于 PortSwigger 官方研究《HTTP/1.1 Must Die: The Desync Endgame》
https://portswigger.net/research/http1-must-die

HTTP/1.1 是一个基于文本的协议,且请求边界没有明确分隔符,仅依赖 Content-Length、Transfer-Encoding 等头部来判断长度。协议中没有强制的消息帧边界。

现代web的消息路径一般是:

1
浏览器 → CDN → WAF → 反向代理 → 应用服务器

如果不同组件的解析规则不一致,Desync攻击就变得非常容易。

Desync 攻击是如何构造的?

下面以经典CL.TE为例:

在 HTTP/1.1 中,有两个主要的头部用来告知服务器请求体的长度:

  1. Content-Length (CL): 直接以字节数表示请求体的长度。
  2. Transfer-Encoding: chunked (TE): 告知服务器数据是分块传输的,每块前面有一个数字,表示数据长度,最后以一个 0 长度的块结束。

当前端服务器和后端应用服务器对 HTTP 请求定界(判断请求在哪里结束)的逻辑不一致时,攻击者可以“走私”一部分请求内容到下一个正常的请求中。

构造恶意请求: 攻击者发送一个同时包含 Content-Length 和 Transfer-Encoding: chunked 的请求。

1
2
3
4
5
6
7
8
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 15
Transfer-Encoding: chunked

0

SMUGGLED
  • 前端(CL)视角: 前端组件看到 Content-Length:15 。它数了数,发现从 0 到 SMUGGLED\r\n 刚好是 15 个字节(含换行)。于是它把这全部内容转发给后端。
  • 后端(TE)视角: 后端组件看到 Transfer-Encoding: chunked。它开始读分块,读到第一个分块长度是 0,它就认为:“OK,这个请求到此结束了。”

注意:这里的前端/后端并不是指浏览器/服务器,而是指“浏览器 → CDN → WAF → 反向代理 → 应用服务器”这一路径上的前后两个组件。

  • 结果: 后端处理完第一个请求并返回。但此时,字符串 SMUGGLED 仍然留在服务器的连接缓冲区里。
  • 后果: 当下一个正常用户发送 GET /index.html 时,后端实际接收到的是:
1
2
3
SMUGGLED
GET /index.html HTTP/1.1
Host: ...

这通常会导致 404 错误、权限绕过,精心构造的SMUGGLED字符串甚至可以劫持用户的会话。

CL.TE漏洞

HTTP1.1中,为什么Desync问题不易修复

我们还是以CL.TE为例,厂商在A打补丁:

1
“如果同时存在 CL 和 TE,统一按 CL 解析”

厂商B打补丁:

1
“如果同时存在 CL 和 TE,统一按 TE 解析”

各厂商的修复策略不一样,可能产生新的desync。然后又打上新的补丁。

1
2
3
4
if (hasCL && hasTE && isProxy) { ... }
else if (hasCL && !hasTE && legacyClient) { ... }
else if (hasTE && httpVersion < 1.1) { ... }
else if (strictMode) { ... }

而 CDN、WAF、代理、负载均衡、服务器。 全部在独立演化这些规则。

攻击者只需要找到一条路径,即可实现攻击。

HTTP/2如何修复此问题

以上问题的出现,问题不在某条代码,而在协议模型本身:

HTTP/1.1 特性 安全后果
没有帧边界 必须靠推断请求结束位置
文本协议 解析规则复杂、易产生歧义
向后兼容包袱 无法强制统一解析行为
多组件链路 任意两层差异即可被利用

只要还在用 HTTP/1.1, 就必然存在某个角落的解析差异。
HTTP/2 把请求彻底改成了二进制帧协议,每一帧的结构是固定的。

1
2
3
4
5
6
7
+-----------------------------------------------+
| Length (24) | Type (8) | Flags (8) |
+-----------------------------------------------+
| Stream Identifier (31 bits) |
+-----------------------------------------------+
| Frame Payload (Length bytes) |
+-----------------------------------------------+

核心安全点

  • Length 字段明确规定帧长度
  • 解析器按 Length 精确切帧
  • 不需要 Content-Length / TE
  • 多路复用在帧层完成

解析过程是数学确定的,没有猜测空间。