把一条请求藏进另一条里、再由后端放出的攻击。

请求走私把代理链路带偏

一条 HTTP/1.1 请求并不会自己说明它在哪里结束。它的边界是算出来的——靠 Content-Length,或者靠 chunked 传输编码——而链路上的每一跳都在各自独立地算。请求走私,就是两跳从同一串字节里算出了不同答案时发生的事。 RFC 9112 后来把规则写明了:Transfer-Encoding 压过 Content-Length;服务端可以拒绝同时带两者的报文;无论怎么选,之后都必须关闭连接。这在纸面上堵住了缝。真实的链路里仍然有早于这条规则的一跳、下游只会说 HTTP/1.0 的一跳,以及一个在入口终结 HTTP/2、再向内重新按 HTTP/1.1 组帧的边缘层。攻击就住在这些分歧里。 两个解析器,一条消息边界 组帧规则是有优先级的。若存在 Transfer-Encoding 且 chunked 是最后一个编码,报文体一直读到那个长度为 0 的终止块为止;否则一个合法的 Content-Length 给出字节数;两者都没有的请求没有报文体。当两个头同时出现,规范说 Transfer-Encoding 胜出——但只读 Content-Length 的那一跳会提前停下、把余下的字节转发出去,而那些余下的字节就是攻击。 实验把两者放在真实的 socket 上:前端按 Content-Length 组帧、原样转发字节,后端按 Transfer-Encoding 组帧。攻击者的 POST 同时声明 Content-Length: 57 与 Transfer-Encoding: chunked,报文体是 0\r\n\r\n 再加一条完整的请求。对前端来说那是一个 57 字节的报文体;对后端来说,chunked 报文体是零字节,后面那条请求是一条新报文。一条请求变成了两条,而前端从未见过后一条。 ...

十月 19, 2027 · 1 分钟 · XB Field Notes