分析自建高防 CDN 应对 HTTP 慢速攻击的超时机制设定
摘要:# 自建高防CDN,慢速攻击来了怎么设“超时”?这事儿真得琢磨透 说个你可能遇到过的情况:网站突然变卡,CPU莫名飙升,但流量看着也没多大。查日志吧,连接数倒是不少,可每个请求都慢悠悠的,半天传不完一个数据包。这时候,十有八九是被HTTP慢速攻击给盯上了…
自建高防CDN,慢速攻击来了怎么设“超时”?这事儿真得琢磨透
说个你可能遇到过的情况:网站突然变卡,CPU莫名飙升,但流量看着也没多大。查日志吧,连接数倒是不少,可每个请求都慢悠悠的,半天传不完一个数据包。这时候,十有八九是被HTTP慢速攻击给盯上了。
我自己帮人看过不少这种“疑难杂症”,很多站点的运维一开始都以为是程序bug或者资源不够,加服务器、优化代码折腾一圈,最后发现,问题出在防护策略上——尤其是超时机制没设对。
说白了,慢速攻击玩的就是“磨洋工”。它不像DDoS那样用洪水冲垮你,而是用少量连接,慢慢耗光你的服务器资源(比如连接池、线程池)。对付这种“慢性毒药”,光靠堆带宽、加机器是没用的,关键得在连接管理上做文章。
慢速攻击到底在“慢”什么?
先别急着调参数,咱们得搞清楚对手的套路。常见的HTTP慢速攻击,主要有这几种“慢法”:
- Slowloris(慢速loris): 这算是祖师爷级别的了。攻击者发起大量HTTP连接,但只发送请求头,不发送完整的请求(或者以极慢的速度发送请求体)。你的服务器得一直等着它把话说完,一个连接可能被挂起几十分钟,直到超时。连接数一满,正常用户就进不来了。
- Slow POST/Read: 这次是请求头发完了,但在发送POST数据体(Body)时磨洋工。比如,它声明要上传一个1MB的文件,然后每秒只发1个字节。你的服务器(或CDN节点)同样得为这个连接保留资源,干等着。
- Slow Read: 反过来玩。它快速发送一个完整的请求,但在接收服务器响应时,把自己的接收窗口(TCP RWIN)设得非常小,或者以极慢的速度确认(ACK)数据包。导致服务器发送响应数据的速度被强行拖慢,大量发送缓冲区被占着。
这类攻击的阴险之处在于,它看起来太“正常”了。 流量不大,IP可能也不集中,传统的基于流量阈值的防护策略很容易漏过去。很多所谓的高防方案,PPT上吹得天花乱坠,真遇到这种精细化攻击,可能直接就露馅了——因为它们默认的超时设置,往往是为正常业务优化的,而不是为对抗恶意连接设计的。
自建高防CDN,超时机制怎么设才不“坑”自己?
好了,重头戏来了。你自己搭高防CDN(比如用Nginx、OpenResty、HAProxy等),面对慢速攻击,超时参数就是你的“手术刀”。但这里有个核心矛盾:设得太短,可能误伤正常慢速用户(比如移动网络差的人);设得太长,又等于给攻击者开了后门。
我的经验是,分层、分阶段地设置超时,而不是一个参数管到底。下面我以最常用的Nginx为例,聊聊具体的思路(其他软件原理相通)。
第一层:连接建立阶段——别让握手变成“拉手”
攻击者在建立TCP连接时可能就会开始拖时间。这里的关键是 client_header_timeout 和 client_body_timeout,但在这之前,有个更基础的。
timeout参数(在http或stream模块): 这个是比较通用的连接超时。对于面向公网的CDN边缘节点,我通常会把建立连接的超时设得比较激进。比如:http { # 等待客户端发送请求头的超时时间,默认60秒太长了! client_header_timeout 10s; # 等待客户端发送请求体的超时时间,同样要缩短 client_body_timeout 10s; # 发送响应给客户端的超时时间 send_timeout 10s; # 一个更全局的保活连接超时,可以设短点 keepalive_timeout 30s; keepalive_requests 100; }为什么是10秒? 对于一个正常的HTTP请求,10秒内连头都发不完,那基本可以判定为异常了(除非是超大文件上传,但那属于特殊情况,需要单独配置)。这个值可以根据你业务的真实情况微调,比如对API服务可以更短(5秒),但对一些教育类可能有长轮询的页面可以稍长(15-20秒),但绝对不要再用默认的60秒。
第二层:请求读取阶段——给“慢吞吞”上发条
这是对抗Slowloris和Slow POST的核心。除了上面的 client_header_timeout 和 client_body_timeout,还要注意:
client_max_body_size: 这个必须设! 而且要设一个合理的值。比如你的业务最大上传文件是100M,那就设成100M或稍大一点。别用默认的1G或者不设限,否则攻击者声明上传一个10G的文件然后慢慢发,你哭都来不及。client_body_buffer_size和client_header_buffer_size: 设置合理的缓冲区大小。如果攻击者发送的头部或体数据超过了缓冲区,Nginx会开始写临时文件,这会消耗磁盘I/O。设小一点可以迫使一些攻击提前暴露,但要注意别影响正常大请求。
第三层:上游通信阶段——保护你的源站
你的CDN节点(代理)和源站服务器之间的超时同样重要。如果CDN节点被慢速攻击拖住,它可能会占用大量到源站的连接,把压力传导到源站。
proxy_connect_timeout: CDN节点连接源站的超时,可以设短点(如5秒)。proxy_send_timeout: CDN节点向源站发送请求的超时。这个要参考你源站处理请求的通常时间,但也要考虑攻击场景。可以比client_body_timeout稍长一点,比如15秒。proxy_read_timeout: 这是重中之重! CDN节点等待源站响应的超时。对于慢速读攻击,攻击者拖慢的是它自己接收数据的速度,但CDN节点从源站读取数据的过程,如果源站响应很快,CDN节点会很快读完并缓存。这个超时主要是防止源站自身处理慢。关键在于,CDN节点要能识别客户端接收慢的情况,并主动中断连接。 这需要更精细的模块或策略,比如OpenResty中可以通过Lua脚本监控客户端的数据接收速度,如果低于某个阈值一段时间,就主动ngx.exit(444)关闭连接。
一个容易被忽略的“神器”:连接速率限制
单纯的超时有时还不够“主动”。我强烈建议在自建高防CDN中,启用连接速率和请求速率限制。这就像给水管加了个水表,水流太慢(长时间低速率)或太怪(忽快忽慢)都能发现。
在Nginx中,可以用 limit_conn 和 limit_req 模块,但更精细的控制可能需要结合 ngx_http_limit_conn_module 和 ngx_http_limit_req_module,或者用OpenResty写Lua逻辑:
- 限制单个IP的连接数(
limit_conn_zone和limit_conn):这是基础,能防止单个IP开太多连接来慢速消耗你。 - 限制请求速率(
limit_req_zone和limit_req):对于慢速攻击,它可能请求速率不高,但可以设置一个“最低速率”的变种思路。比如,如果一个连接建立后,在10秒内连一个完整请求都没发完,就把它加入黑名单一段时间。 - 动态限速: 通过Lua脚本,实时计算每个连接的“数据接收/发送平均速率”。如果低于一个阈值(比如1KB/s)持续超过20秒,就判定为恶意慢速连接,直接切断。这才是对抗慢速攻击的“智能”手段。
说点大实话:没有银弹,只有组合拳
看到这里你可能觉得,哇,参数好多好复杂。没错,对抗慢速攻击,尤其是用自建方案,本质上是一场资源消耗和策略精细度的博弈。
- 别指望一个参数搞定所有。 你需要根据业务日志,分析正常用户的行为模式(请求头大小、请求体大小、完成时间),然后设定一个比正常范围稍宽松,但远小于默认值的超时门槛。
- 监控和日志是关键。 一定要详细记录那些因为超时被断开的连接(Nginx的
error_log设成info或debug级别)。看看是谁、为什么被断开。是误伤了新疆用2G网络的大叔,还是真的抓住了攻击者? - 自建CDN的“隐藏成本”。 这些精细化的配置、调试、监控和维护,就是自建高防CDN的隐藏成本。它给你带来了极致的灵活性和可控性,但也要求你的团队有足够的技术深度和运维精力。不然,很可能出现“攻击来了,规则没生效;规则生效了,业务也挂了”的尴尬局面。
最后给个结论吧(虽然说不总结,但总得有个收尾):对付HTTP慢速攻击,自建高防CDN的超时设定,核心思路就一个——变被动等待为主动管理。 把连接的生命周期拆开,在每个阶段都装上“计时器”和“速度表”,一旦发现有人想用“慢”字诀耗死你,就果断请它出去。这活儿不轻松,但一旦调好了,你的网站在面对这种阴险攻击时,会稳当得多。
行了,参数是死的,业务是活的。赶紧去看看你那边CDN的配置吧,说不定某个角落还躺着默认的60秒超时呢。

