HTTP DoS By Slow Request
HTTP DoS By Slow Request
Slow DoS 就是以一个极慢的速率发送 HTTP 请求报文,长时间占用服务器的处理进程/线程,通过在短时间内大量重复此行为,让服务器无法为正常请求分配服务处理进程/线程,导致 DoS。
以下测试服务器版本:
httpd Apache/2.4.52 (Unix)
nginx version: nginx/1.20.2
Slowloris
Slowloris攻击是一种针对web服务器的慢速HTTP攻击,由安全研究人员在2009年提出。
在HTTP协议中规定,HTTP头部以连续的 “\r\n\r\n
” 作为结束标志,许多Web服务器在处理HTTP请求的头部信息时,会等待头部传输结束后再进行处理,因此,如果Web服务器没有接受到连续的 “\r\n\r\n
”,就会一直接受数据并保持与客户端的连接,利用这个特性,攻击者能够长时间与Web服务器保持联系,并逐渐耗尽Web服务器的连接资源。
步骤
- 攻击者首先通过发送多个部分 HTTP 请求标头来打开与目标服务器的多个连接。
- 目标为每个传入请求打开一个线程,目的是在连接完成后关闭该线程。为提高效率,如果连接花费时间太长,服务器将使非常长的连接超时,从而为下一请求释放线程。
- 为了防止目标使连接超时,攻击者会定期向目标发送部分请求标头,以使请求保持活动状态。
- 目标服务器在等待请求终止时永远无法释放任何打开的部分连接。一旦所有可用线程都被使用,服务器将无法响应来自常规流量的其他请求,从而导致拒绝服务。
测试工具
slowloris-py
usage:
➜ slowloris-py --help
usage: slowloris-py [-h] [-p PORT] [-s SOCKETS] [-v] [-ua] [-x] [--proxy-host PROXY_HOST] [--proxy-port PROXY_PORT] [--https] [--sleeptime SLEEPTIME] [host]
options:
-h, --help show this help message and exit
-p PORT, --port PORT Port of webserver, usually 80
-s SOCKETS, --sockets SOCKETS
Number of sockets to use in the test
-v, --verbose Increases logging
-ua, --randuseragents
Randomizes user-agents with each request
-x, --useproxy Use a SOCKS5 proxy for connecting
--proxy-host PROXY_HOST
SOCKS5 proxy host
--proxy-port PROXY_PORT
SOCKS5 proxy port
--https Use HTTPS for the requests
--sleeptime SLEEPTIME
Time to sleep between each header sent.
例:利用工具 python slowloris.py ,创建 2048 个 socket 去发送 http 请求,每个标头延时6秒:
slowloris-py -s 2048 --sleeptime 6 127.0.0.1
从源码可以看出为建立指定数量 TCP 连接,对每个 socket 发送请求方法,
User-Agent
以及Accept-Language
标头后,每隔指定时间就遍历所有 socket 并发送X-a
标头。很简单的一个小工具,单线程,发送固定的标头,只能作为一个对小型服务应用的测试工具。
在这期间以及停止 slowloris 后的很长一段时间(取决于创建的socket数量) httpd 会产生大量的
internal dummy connection
日志:原因是 apache 服务器会对子进程进行监听,会向子进程发送 OPTIONS 请求,通常这种请求隔很长时间才会发送一次,但 slowloris 工具创建了大量连接,apache 服务器为其创建了大量进程,数量一多,该日志就写入的比较频繁了。
可以通过以下方法过滤该日志:
SetEnvIf User-Agent "(internal dummy connection)" dontlog CustomLog logs/access_log common env=!dontlog
也可以直接过滤掉h环回地址访问产生的日志:
SetEnvIf Remote_Addr "127\.0\.0\.1" loopback CustomLog logs/access_log common env=!loopback
slowhttptest
slowhttptest 是一个测试 slow HTTP DoS vulnerabilities 的工具。
usage:
slowhttptest, a tool to test for slow HTTP DoS vulnerabilities - version 1.8.1
Usage: slowhttptest [options ...]
Test modes:
-H slow headers a.k.a. Slowloris (default)
-B slow body a.k.a R-U-Dead-Yet
-R range attack a.k.a Apache killer
-X slow read a.k.a Slow Read
Reporting options:
-g generate statistics with socket state changes (off)
-o file_prefix save statistics output in file.html and file.csv (-g required)
-v level verbosity level 0-4: Fatal, Info, Error, Warning, Debug
General options:
-c connections target number of connections (50)
-i seconds interval between followup data in seconds (10)
-l seconds target test length in seconds (240)
-r rate connections per seconds (50)
-s bytes value of Content-Length header if needed (4096)
-t verb verb to use in request, default to GET for
slow headers and response and to POST for slow body
-u URL absolute URL of target (http://localhost/)
-x bytes max length of each randomized name/value pair of
followup data per tick, e.g. -x 2 generates
X-xx: xx for header or &xx=xx for body, where x
is random character (32)
-f content-type value of Content-type header (application/x-www-form-urlencoded)
-m accept value of Accept header (text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5)
Probe/Proxy options:
-d host:port all traffic directed through HTTP proxy at host:port (off)
-e host:port probe traffic directed through HTTP proxy at host:port (off)
-p seconds timeout to wait for HTTP response on probe connection,
after which server is considered inaccessible (5)
-j cookies value of Cookie header (ex.: -j "user_id=1001; timeout=9000")
Range attack specific options:
-a start left boundary of range in range header (5)
-b bytes limit for range header right boundary values (2000)
Slow read specific options:
-k num number of times to repeat same request in the connection. Use to
multiply response size if server supports persistent connections (1)
-n seconds interval between read operations from recv buffer in seconds (1)
-w bytes start of the range advertised window size would be picked from (1)
-y bytes end of the range advertised window size would be picked from (512)
-z bytes bytes to slow read from receive buffer with single read() call (5)
其实发现该工具支持:
- slow headers(
-H
):就是 slowloris; - slow body(
-B
):slow post - range attack(
-R
):针对 apache 服务器的工具; - slow read(
-X
):即以缓慢的速度读取服务器的响应,保持与服务器的连接;
该工具默认为 slowloris 模式。
服务器相关配置
httpd
请求处理数量
mpm_prefork_module
、mpm_worker_module
以及 mpm_event_module
模块定义了同一时间内最大进程(其中 worker、event 模块还涉及到线程数量),以及最大请求数量。
例:prefork 模块相关配置
LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
<IfModule mpm_prefork_module>
StartServers 5
MinSpareServers 5
MaxSpareServers 10
MaxRequestWorkers 250
MaxConnectionsPerChild 1000
</IfModule>
Web服务器能够处理的并发连接数是有限的,如果攻击者利用大量的受控主机发送这种不完整的HTTP GET请求并持续占用这些连接,就会耗尽Web服务器的连接资源,导致其他用户的HTTP请求无法被处理,造成拒绝服务。
超时
apache httpd 提供了 reqtimeout_module
来设置超时和接收请求的最小数据速率。如果发生超时或数据速率太低,服务器将关闭相应的连接。
RequestReadTimeout [handshake=timeout[-maxtimeout][,MinRate=rate] [header=timeout[-maxtimeout][,MinRate=rate] [body=timeout[-maxtimeout][,MinRate=rate]
该指令可应用在基础配置或 VirtualHost
中。
默认:
RequestReadTimeout handshake=0 header=20-40,MinRate=500 body=20,MinRate=500
值为 \(0\) 表示没有超时限制。
该指令可以设置各种超时来完成TLS握手,从客户端接收请求标头或请求正文。如果客户端未能在配置的时间内完成所有这些阶段,408 REQUEST TIME OUT
则会发送错误。
对于SSL虚拟主机,handshake
超时值是执行初始SSL握手所需的时间。如果将用户的浏览器配置为查询证书吊销列表,并且无法访问CRL服务器,则初始SSL握手可能要花费相当长的时间,直到浏览器放弃等待CRL为止。因此,handshake
对于SSL虚拟主机,超时应考虑到这种可能的开销(如有必要)。主体超时值包括SSL重新协商所需的时间(如有必要)。
当 AcceptFilter
在使用中(通常在Linux和FreeBSD的情况下),套接字未至少一个字节(或整个请求之前发送到服务器的过程 httpready
)被接收。配置的握手和标头超时 RequestReadTimeout
仅在服务器进程收到套接字后才有效。
对于三个超时阶段(握手,标头或正文)中的每个阶段,都有三种指定超时的方法:
- 固定超时值
stage=timeout
:完成整个阶段所需的时间(以秒为单位)(握手,读取所有请求标头或正文)。值为0表示没有限制。 - 禁用虚拟主机模块
handshake=0 header=0 body=0
:这将mod_reqtimeout
完全禁用(请注意,handshake=0
这已经是默认设置,可以省略)。 - 接收数据时增加的超时值
stage=timeout, MinRate=data_rate
:与上面相同,但是无论何时接收到数据,超时值都会根据指定的最小数据速率(以字节/秒为单位)增加。 - 接收数据时增加的超时值,上限
stage=timeout-maxtimeout, MinRate=data_rate
:与上面相同,但是超时不会增加到指定超时范围的第二个值以上。
nginx
请求处理数量
nginx 最大请求处理量被限制为 worker_processes
\(\times\) worker_connections
worker_processes 1; # 进程数量
events {
worker_connections 1024;
multi_accept on; #设置一个进程是否同时接受多个网络连接,默认为off
}
超时
client_header_timeout
- 语法:
client_header_timeout time
- 默认值: \(60\)s
- 上下文:
http
server
- 说明: 指定等待client发送一个请求头的超时时间(例如:GET / HTTP/1.1)。仅当在一次read中,没有收到请求头,才会算成超时。如果在超时时间内,client没发送任何东西,nginx返回HTTP状态码
408 (Request timed out)
。
client_body_timeout
- 语法:
client_body_timeout time
- 默认值: \(60\)s
- 上下文:
http
server
- 说明: 该指令设置请求体(request body)的读超时时间。仅当在一次 read 中,没有得到请求体,就会设为超时。超时后,nginx返回HTTP状态码
408 (Request timed out)
。
keepalive_timeout
-
语法:
keepalive_timeout timeout [ header_timeout ]
-
默认值: \(75\)s
-
上下文:
http
server
location
-
说明: 第一个参数指定了与client的keep-alive连接超时时间。服务器将会在这个时间后关闭连接。可选的第二个参数指定在响应头
Keep-Alive: timeout=time
中的time值。这个头能够让一些浏览器主动关闭连接,这样服务器就不必要去关闭连接了。没有这个参数,nginx不会发送Keep-Alive响应头(尽管并不是由这个头来决定连接是否“keep-alive”)。两个参数的值可并不相同。注意不同浏览器怎么处理“keep-alive”头:
-
MSIE和Opera忽略掉"Keep-Alive: timeout=
" header. -
MSIE保持连接大约60-65秒,然后发送TCP RST
-
Opera永久保持长连接
-
Mozilla keeps the connection alive for N plus about 1-10 seconds.
-
Konqueror保持长连接N秒
这个其实与 Slow DoS 没关系,keepalive 是在一次请求之后,不会关闭TCP连接,之后会复用。而 Slow Dos 只是发生在一次请求之中的攻击。
-
对服务器的测试
apache httpd
Content-length
为 \(10000\),每次最多发送 \(68\) 字节的 Body,每 \(5\) 秒发送一次 Body 数据,对 httpd 服务(:8080
,prefork 默认配置)进行测试:
slowhttptest -c 1024 -r 1024 -s 10000 -i 5 -u http://127.0.0.1:80
大概 \(20\)s (apache httpd 默认超时时间 \(20\) s)后,httpd 会响应 \(408\) ,少部分为 \(200\):
http 错误日志中也会有 timeout
错误:
[Thu Apr 14 23:41:49.134734 2022] [reqtimeout:info] [pid 35915] [client 127.0.0.1:38180] AH01382: Request header read timeout
[Thu Apr 14 23:41:49.134760 2022] [core:debug] [pid 35915] protocol.c(1479): [client 127.0.0.1:38180] AH00567: request failed: error reading the headers, referer: TESTING_PURPOSES_ONLY
从结果上可以看出,在 \(108\) 秒内,apache httpd 就关闭了全部的 \(1024\) 个请求。也就是说 apache httpd 不会无限地去等待客户端把完整报文发送过来。
显示还有24个 connected 是因为没更新输出,通过
-g
将结果输出到文件中可以看到全部都被 closed 了。
虽然不会无限等待,但是根据默认配置会,等待 20 s!,
中间用浏览器访问很难得到响应:
nginx
注释掉 nginx 配置中的 proxy_pass
,并测试:
➜ slowhttptest -c 1024 -r 1024 -s 10000 -i 5 -u http://127.0.0.1:80
大概一分钟之后(nginx 默认超时时间为 \(60\) s),nginx开始输出日志,即返回 \(408\):
120 秒后,1024个连接全部中断:
nginx 反向代理
对 nginx (:80
,8 processes
\(\times\) 2048 connections
)反向代理的 httpd(:8080
,prefork 默认配置)进行测试:
➜ slowhttptest -c 1024 -r 1024 -s 10000 -i 5 -u http://127.0.0.1:80
情况与 nginx 测试类似。
httpd 服务的日志中,中间基本上都是 \(408\),最后的十几个响应为 \(200\):
在以上三个测试中,httpd 与 nginx 服务器都会去断开超时的连接。
Slow Post
慢速POST请求攻击也是一种针对Web服务器的慢速HTTP攻击,由安全研究人员在2010年提出,与Slowloris攻击不同的是,慢速POST请求攻击利用缓慢发送HTTP BODY的方式达到占用并耗尽Web服务器连接资源的目的。
在HTTP头部信息中,可以使用 Content-Length
字段来指定HTTP消息实体的传输长度,当Web服务器接收到的请求头部中含有 Content-Length
字段时,服务器会将该字段的值作为HTTP BODY的长度,持续接收数据并在达到 Content-Length
值时对HTTP BODY的数据内容进行处理,利用这个特性,攻击者能够长时间与Web服务器保持连接,并逐渐耗尽Web服务器的连接资源。
攻击者在发送HTTP POST请求时,在请求头部中将 Content-Length
设置为一个很大的值,并将HTTP BODY以非常缓慢的速度一个字节一个字节的向Web服务器发送,这样,Web服务器就需要一直维持与客户端的连接并等待数据传输结束,攻击者就可以长时间占用这个连接,通过间断性的发送单字节的HTTP BODY内容,攻击者就能够确保连接不因超时而导致中断,如果攻击者利用大量的受控主机发送这种缓慢的HTTP POST请求并持续占用这些连接,就会耗尽Web服务器的连接资源,导致其他用户的HTTP请求无法被处理,造成拒绝服务。
apache httpd
与 Slowloris 测试类似,通过添加参数 -B
来慢发送 Http Request Body,结果几乎与 Slowloris 测试相同。
nginx
注释掉 nginx 配置中的 proxy_pass
,并测试:
➜ slowhttptest -c 1024 -r 1024 -s 10000 -i 5 -u http://127.0.0.1:80 -B
\(45\)s 将所有的连接关闭,但是原因是 nginx 不允许使用 POST
方式获取数据,会返回 \(405\) (Method Not Allowed)🤔
nginx 反向代理
对 nginx (:80
,8 processes
\(\times\) 2048 connections
)反向代理的 httpd(:8080
,prefork 默认配置)进行测试:
➜ slowhttptest -c 1024 -r 1024 -s 10000 -i 5 -u http://127.0.0.1:80 -B
发现 nginx 反向代理 apache httpd 的情况下,Slow Post 使得全部保持连接,最终测试超时。
再试试 1 个请求时的情形:
➜ slowhttptest -c 1 -r 1 -s 10000 -i 5 -u http://127.0.0.1:80 -B
依然没有断开连接:
通过修改 nginx 配置,使各个 timeout 为比较小的值(其实 keeplive_timeout
可以不用设置,就只是一次 HTTP 请求):
➜ slowhttptest -c 1024 -r 1024 -s 10000 -i 5 -u http://127.0.0.1:80 -B
nginx 关闭全部连接。
防御 Slow Dos
利用第三方平台
例如 Cloudflare 会先缓存所有到达的 request
Cloudflare buffers incoming requests before starting to send anything to the origin server. As a result, “low and slow” attack traffic like Slowloris attacks never reach the intended target. Learn more about how Cloudflare's DDoS protection stops slowloris attacks.
适当优化服务器配置
适当的配置服务器的最大进程/线程以及最大处理处理数量。
不过该方法还是可能会被攻击者制造的大量伪请求占用过多线程,且开辟大量线程/经常也会对其调度造成影响。
也可以对服务器 timeout
参数进行配置,但是本身服务器很难判断是 slowloris 还是真的网速慢,这个值需要根据具体的场景来把握。
对ip进行统计并屏蔽
在服务器层面或者服务器以下层对IP进行统计与屏蔽。因为要造成 slowloris,最起码双方已完成 TCP 连接,所以攻击者提供了真实的IP(不一定是攻击者本身的IP,可能利用了代理),所以需要在这些层对IP进行统计,并进行处理。
参考: