可以将HTTP / 1.1请求拆分为多个TCP消息吗?

时间:2020-02-08 07:19:16

标签: http

我正在阅读有关Slowloris的文章,我不确定为什么会受到攻击,因为客户端一次向服务器发送一个标头。服务器是否不希望仅收到一条TCP消息/请求,但第二条消息可能会在正文连续100个后出现,或者如果使用分块编码则超过2个,可能不会包含第二个?可以一次发送一个字节的HTTP请求吗?如果是的话,我应该在read()调用之后的每个字节之后发送100个继续吗?我很难找到记录在哪里。这对我来说尤其重要,因为我正在尝试从头开始用C构建HTTP服务器,并且我想知道是否需要逐个字符进行解析。

1 个答案:

答案 0 :(得分:1)

从TCP获取数据

TCP不传送消息。它传递字节流,并且可以将这些字节传递到包裹中,这些包裹的大小不必与发件人所写包裹的大小相似。因此,每当从TCP读取数据流时,都必须准备好将数据打包成小包,并分散在套接字上的多个read()调用中。

通常,接收器会将这些包裹中的数据累积到缓冲区中,直到它收集到足够的数据以构造某种有意义的数据单元,然后再进行处理。根据数据的格式和含义,可操作数据的单位可以是一定数量的字节,或者一行的字符数,或者(可能是多字节)字符,或其他。

对于HTTP服务器,可操作的单元可以合理地是一整套标头,通过标头块结尾的空行可以识别该标头。或者,您可以确定第一个可操作单元是一行(HTTP请求行),而第二个可操作单元是其余的标头集。 (如果您决定在处理标头时可操作的单元始终是一行,则必须准备处理多行标头。这可能需要附加的缓冲层。)

在通常情况下,如果指定的缓冲区足够容纳它们,则来自HTTP连接上的TCP的数据将以相当大的块,单个read()的数百甚至数千个字节到达。因此,可能是所有标头都由新连接上的第一个read()收集的情况。但是您一定不要依赖这种情况。您必须准备发出多个read()调用以获取所需的数据。

即使您必须进行多次read()调用才能获取所有标头,通常花费的时间也很短,然后您就可以继续处理此请求并继续下一个请求了

资源枯竭

每当HTTP服务器正在处理一个请求时,它将有一定数量的资源专用于该请求-它具有连接套接字,数据缓冲区,可能分配了一些用于跟踪请求状态的结构,也许有一个或多个线程用于此连接或请求,TLS上下文(如果这是HTTPS请求)等等。如果可以快速处理该请求,则可以释放这些资源并对其进行回收或将其应用于其他请求和连接。如果无法快速处理请求,则这些资源将被占用,并且如果服务器必须处理许多长期存在的请求,则其资源需求可能会上升到服务器速度缓慢或无法接受新连接的地步,甚至崩溃。

如果服务器的大小不足以承受负载,则在正常操作中可能会发生资源耗尽,但是攻击者也有可能故意将服务器耗尽。这种攻击有多种变体。第一个HTTP服务器耗尽攻击涉及使一个敌对的客户端发送非常大(也许无限大)的请求主体,和/或非常缓慢地发送那些请求主体,和/或保持连接打开而根本不发送任何请求数据。服务器针对这种攻击的防御措施是限制它将消耗的请求主体的大小,并限制其等待请求主体完全交付的时间长度,并限制其等待的时间长度接收一定数量的请求数据。

还有一种攻击是客户端打开与服务器的连接,但根本不发送任何头数据。服务器的防御是限制等待头数据开始到达的时间,并限制等待其他头数据到达的时间。

Slowloris

Slowloris攻击是基于头的精疲力尽攻击进展中的下一步。它不发送任何标头,而是发送标头,但发送速度非常慢,刚好足够慢以避免服务器断开连接。

并且Slowloris发送了一系列无休止的虚假头文件,因此服务器永远不会进入针对可笑的请求主体攻击的防御措施。如果想要浪费服务器上更多的CPU,攻击者甚至可以一次发送这些标头。我不知道原始的Slowloris是否可以做到这一点,但是如果以后有某种变体,我也不会感到惊讶。

通常,针对Slowloris的服务器端防御措施是应用与恶意请求主体相同的策略。也就是说,在请求标头上设置到达完成时间和标头总大小限制。

100-继续

100-Continue无关。您的服务器应准备在处理请求标头之后发送100-Continue临时响应,但前提是客户端明确指定(通过发送要求Expect的{​​{1}}标头)临时回应。根据我的经验,这种情况很少见,因此,如果您只是开始构建服务器,我就不必担心太多。在基本服务器启动并运行后,添加此功能应该很简单。

文档

RFC 2616是所有HTTP 1.1的指定位置。如果您正在编写服务器,则应首先非常熟悉该RFC。

技术上,RFC 2616已被一系列更高版本的RFC(72307231723272337234,{{3} }),但更改很小,我发现阅读一个旧文档要比阅读五个新文档容易。