HTTP 流水线:在请求主体完成之前产生响应

HTTP 流水线:在请求主体完成之前产生响应

我知道 HTTP 客户端即使尚未收到先前请求的响应,也有可能通过 TCP 连接发送下一个请求 - 这称为 HTTP 流水线。

但是,HTTP 服务器在请求完全接收之前就开始传输响应是否可以接受?是否有 RFC 来管理此行为?

例如,假设一个服务通过 HTTP POST 接收一些数据,进行一些转换,并使用转换后的数据进行响应。如果转换可以以流式方式完成,那么即使请求主体不完整,服务器也可以开始生成响应主体。

答案1

如果标准明确描述并认可这样的操作,我会感到惊讶(因为它消失地(虽然很少能确定收到的部分请求包含足够的信息来开始生成响应),但我认为在大多数情况下它都能正常工作。在客户端完成发送请求之前,您发送的任何响应数据几乎肯定不会被客户端读取,因此它会停留在读取缓冲区中,直到客户端开始读取,此时它会想,“天哪,速度真快!”并开始愉快地咀嚼。它不会意识到数据已经在建筑物内了……

我确信有一些奇怪的 HTTP 客户端实现会在其发送循环中做出奇怪的事情,例如,如果它在完成写入请求之前发现套接字可读,它就会发疯,但找到这些事情的唯一方法是尝试一下,看看会有什么问题。也就是说,HTTP 领域的一切基本上都是这样完成的——以及如何制定新的标准行为(有人做了一些疯狂的事情,变得足够流行,以至于其他人不得不适应,因此 IETF WG 说“好吧,那我们就把这种行为作为标准吧”)。

答案2

我要补充@wombie 的回答,这样做会非常危险。

当 http 代理(比如说反向代理或负载均衡器,位于浏览器和最终服务器之间)收到响应时,它通常假定查询已完全发送,并且可以发回下一个查询。

大多数反向代理在将查询传输到最​​终后端时不会使用管道。这意味着即使浏览器试图执行管道n查询反向代理和后端之间的通信通常是n 个人询问和答复,等待每个响应后再发送下一个查询(可以使用保持活动连接,但通常不使用流水线)。

这种行为大大降低了HTTP走私、HTTP请求或响应攻击的风险。

在收到整个正文之前发送您的回复将有助于走私攻击。

为了更清楚起见,假设你正在发送一个单一查询,语法奇怪,正文很大。这个正文内容看起来像另一个带有正文的查询。这个正文中的查询的正文是另一个第三个查询。但这只是一个带有正文的查询。

反向代理有一个奇怪语法上的错误并且看不到第一个查询的主体。对于代理来说,这是 2 个查询的管道。因此,它将第一个查询(不带主体)发送到后端。

后端服务器没有奇怪的语法错误, 所以就是等待尸体但它发送了一个早期反应... 我们会开始遇到问题。如果你等到正文完整后再发送响应,攻击就会停止。

反向有一个响应,因此它认为它可以继续下一个查询,并将伪第二查询(包含隐藏的第三个查询的主体)发送回后端。

后端认为这只是第一个查询的主体,并对其进行处理或不处理。但对于后端来说,这不是查询。假设这个主体的大小为 20k,后端正在等待 10k 的主体。最后的 10k 是后端的另一个查询,一个隐藏在主体中的查询,这个查询将被视为另一个查询,并带有新的响应。

代理将收到隐藏的第三个查询的响应,并相信它是第二个查询的响应。

您可以利用这一点进行缓存投毒。早期的响应不足以发起攻击(在这里,您还需要查询上的奇怪语法,代理和后端对主体大小的感知会有所不同),但它使利用变得更加容易。当然,您可以在野外发现这种行为。

答案3

当我尝试在服务器端实现最大文件大小检查时遇到了这种情况。

例如,在 golang 中,发布到服务器的上传文件不是 tmp 目录中某个完全上传的文件,而是在处理时可能仍处于繁忙状态的字节流。如果要停止“洪水”,则必须将此情况告知客户端。例如,http.MaxBytesReader 通过关闭连接、停止读取并发送响应来执行此操作。但客户端没有读取响应,因为它仍在上传其请求主体,并且它不期望此流程(对吗?)。我看到浏览器只是一次又一次地将数据推送到服务器,三次接一次。在浏览器控制台中我看不到这一点,但我可以在服务器端看到它。经过长时间的等待后,浏览器控制台只是报告“空响应”,在此期间,我看到了三次服务器端试验(可能是浏览器特定的)。

简而言之:在向客户端发送响应之前,您必须完整阅读请求正文。

参见https://stackoverflow.com/a/43785190/2814957

模拟这种极端情况比较棘手:你必须确保请求主体足够大,并且服务器使用流式传输。否则,客户端已经上传了所有内容,正在等待响应

相关内容