很多人把CC攻击导致的带宽占满和连接数占满混为一谈,觉得都是网站打不开,表现差不多。实际上,这是两种完全不同的资源耗尽方式,底层逻辑、攻击手法、防御策略都截然不同。如果判断错了方向,比如明明是连接数被打满,你却拼命去加带宽,钱花光了问题依旧。反过来,带宽被占满,你却在服务器里调连接数限制,也是徒劳。搞清楚这两者的本质区别,是精准防御的第一步。
带宽占满的本质是“管道堵死”带宽占满,可以理解为攻击者用海量的垃圾数据把你的网络出口彻底塞死了。你的服务器接入互联网,就像一根水管,带宽就是这根水管的粗细。正常用户访问,相当于涓涓细流。CC攻击制造带宽占满时,攻击者会构造大量超大尺寸的请求,或者请求大文件,比如高清图片、视频、压缩包下载等。每一次请求,服务器都需要向外吐出大量数据包。当成千上万这样的请求并发过来,出口带宽瞬间被占满。这时候,正常用户的数据包根本挤不进去也出不来,表现就是网站访问极慢,甚至完全无法连接。
这种攻击的特点非常明显。服务器本身的CPU和内存负载可能并不高,因为攻击请求往往是静态的、不需要复杂计算的资源。你用终端连上服务器,执行top或者任务管理器查看,发现系统资源还很空闲,但网站就是打不开。再看网络流量监控图,出口流量曲线是一条笔直的横线,死死顶在你的带宽上限。这种攻击的杀伤力在于,它不需要攻破你的应用逻辑,纯粹靠流量暴力撑死你的网络。攻击成本相对较低,尤其是利用反射放大攻击的变种时,攻击者用很小的请求就能换来巨大的流量输出。
连接数占满的本质是“座位抢光”连接数占满,指的是服务器能够同时处理的TCP连接数量被消耗殆尽。每个网络服务,比如Web服务器、数据库,都有最大连接数限制。这就像餐厅里只有固定数量的桌子,攻击者派人进来,每桌只点一杯白开水然后坐着不走,把所有的桌子都占满。后面正常吃饭的客人来了,服务员只能说“客满,请稍等”,但攻击者永远不离开,正常客人就永远进不来。
在CC攻击中,实现连接数占满的常见手法是慢速攻击。攻击者与服务器建立大量连接后,以极其缓慢的速度发送HTTP请求头,或者发送完整的请求后,把数据传输速度控制在极低水平,比如每隔几十秒才发一个字节。服务器必须保持这些连接处于活动状态,等待数据接收完成。攻击者用很小的带宽成本,就能耗尽服务器上宝贵的连接资源。此时,服务器的带宽可能根本没跑满,甚至流量很低,但新的连接请求会被拒绝。你查看服务器状态,会发现大量连接处于ESTABLISHED或者TIME_WAIT状态,而且很多连接的传输速率极低,或者长时间处于数据接收状态。
还有一种更隐蔽的方式,攻击者专门寻找那些处理起来特别慢、特别消耗后端的动态页面。比如复杂的搜索接口、报表生成页面、需要多次数据库交互的API。攻击者用正常速度发送请求,但每个请求都触发服务器执行高消耗操作,导致处理线程长时间被占用。线程池满了,新连接也就无法被处理了。这同样会造成连接数占满的效果,但攻击流量本身并不需要很大。
两种攻击在现象上的关键区别判断你的服务器到底遭遇了哪一种攻击,可以从几个关键指标入手。先看流量图,如果出口流量被打满,持续跑在带宽上限,同时连接数可能高也可能不高,这大概率是带宽消耗型攻击。如果流量并不高,远没达到带宽上限,但网站却无法访问,服务器监控显示连接数异常飙升,或者大量连接卡在特定状态,这就是典型的连接数耗尽型攻击。
再看服务器内部表现。带宽占满时,你登录服务器操作可能都会卡顿,因为SSH或者远程桌面的数据包也在和攻击流量抢带宽。但如果你登录服务器后操作很流畅,只是Web服务无响应,那问题多半出在连接数或者应用层。查看Web服务器的状态页面,比如Nginx的stub_status模块、Apache的server-status,如果发现大量活跃连接正在处理请求,且请求的URL都是同一个大文件或者动态页面,可以辅助判断攻击类型。
还有一个容易被忽略的细节,就是观察网络连接的具体状态分布。使用netstat或者ss命令,统计各状态的连接数量。如果SYN_RECV状态特别多,可能是SYN Flood,这是另一种常见的连接耗尽攻击。如果ESTABLISHED状态特别多,且很多连接的发送队列或接收队列不为零,说明数据在堆积,可能是慢速攻击或者大文件下载型攻击。如果TIME_WAIT状态暴增,可能是短连接攻击,攻击者用大量快速建立又快速断开的连接,迅速消耗掉系统可用的端口号和连接跟踪表项。
针对带宽占满的防御思路防御带宽消耗型CC攻击,核心思路是在流量到达你的服务器出口之前就把它清洗掉。单靠服务器本身的防火墙或者软件,是没办法解决这个问题的,因为攻击流量已经把你的物理带宽占满了,你在服务器上做任何拦截,数据包都已经经过了那根堵死的管道。所以,必须依赖上游的网络防护能力。
最有效的手段是接入高防IP或者高防CDN服务。这些服务商在骨干网上拥有巨大的带宽储备,可以把攻击流量引导到他们的清洗中心,经过识别后,只把正常的请求转发回你的源服务器。你可以在DNS层面把域名解析到高防IP,或者通过CNAME接入高防CDN。配置时要注意隐藏源服务器的真实IP地址,否则攻击者绕过防护直接打源站,所有防护就形同虚设了。源站IP一旦暴露,需要立即更换。
在应用层面,也可以做一些优化来降低带宽消耗。比如对静态资源启用压缩传输,在Web服务器上开启gzip或者brotli压缩,能显著减少传输的数据量。配置合理的缓存策略,让CDN节点和用户浏览器缓存住静态文件,减少回源请求。对于大文件下载类业务,可以强制使用CDN分发,源站不直接提供下载服务。还可以在Web服务器上限制每个连接的最大传输速率,比如Nginx的limit_rate指令,防止单个连接把带宽占满,但这只能作为辅助手段,无法抵御大规模攻击。
针对连接数占满的防御思路防御连接数耗尽型攻击,战场主要在服务器端和应用层。首先要调整操作系统内核的网络参数,增强系统对大量并发连接的处理能力,并加速无用连接的回收。以下几个sysctl参数值得关注:net.ipv4.tcp_syn_retries可以适当减小,降低SYN重试次数。开启net.ipv4.tcp_syncookies有助于防御SYN Flood。net.ipv4.tcp_tw_reuse和net.ipv4.tcp_tw_recycle(在新内核中已被移除,需注意版本兼容性)可以加速TIME_WAIT状态的连接回收。net.core.somaxconn和net.ipv4.tcp_max_syn_backlog需要调大,增加半连接和全连接队列的长度。
在Web服务器和应用层面,必须严格限制单个IP的连接数和请求速率。Nginx可以通过limit_conn模块限制每个IP的并发连接数,通过limit_req模块限制请求速率。以下是一个典型的配置示例:
http {
# 定义共享内存区域,用于存储连接数状态
limit_conn_zone $binary_remote_addr zone=conn_perip:10m;
# 定义请求速率限制区域
limit_req_zone $binary_remote_addr zone=req_perip:10m rate=10r/s;
server {
# 每个IP最多同时保持10个连接
limit_conn conn_perip 10;
# 每个IP每秒最多处理10个请求,允许5个突发
limit_req zone=req_perip burst=5 nodelay;
}
}
这段配置限制了单个IP的并发连接数为10,请求速率为每秒10次。超出限制的请求会被直接拒绝,从而保护后端连接资源不被单个IP耗尽。需要根据实际业务场景调整这些数值,设置过低会误伤正常用户,设置过高则防护效果打折扣。
对于慢速攻击,需要配置客户端超时和传输速率下限。Nginx的client_body_timeout、client_header_timeout指令可以设置接收请求体和请求头的超时时间。send_timeout可以限制向客户端发送响应的超时。更重要的是,可以设置limit_rate_after和limit_rate,强制要求客户端在一定传输量之后必须达到某个最低接收速度,否则断开连接。还可以利用ngx_http_core_module的lingering_close指令,精细控制连接关闭时的行为,清理那些拖着不走的连接。
应用代码层面也要做防御性设计。对于高消耗的动态接口,应该实现异步处理或者队列机制,避免请求线程被长时间阻塞。加入验证码或者人机校验,可以有效拦截自动化攻击工具。在负载均衡层或反向代理层,部署WAF(Web应用防火墙),能够识别并拦截恶意的请求模式,包括那些专门挑选高消耗接口的攻击行为。
混合型攻击的识别与应对现实中,攻击者往往不会只用一种手法。更常见的是混合攻击,既用大流量请求占你的带宽,又用慢速连接耗你的连接数,还可能夹杂着对高消耗接口的精准打击。这种情况下,单一维度的防御很容易顾此失彼。你需要建立多层防御体系,在网络层、传输层、应用层分别部署对应的防护措施。
监控是防御的基础。你需要对带宽使用率、连接数、各状态连接分布、请求速率、后端响应时间等指标进行实时监控,并设置合理的告警阈值。当多个指标同时异常时,能够快速定位攻击类型。比如带宽和连接数同时飙升,且流量主要来自少数几个URL,那就需要同时启用高防清洗和本地连接限制。如果带宽不高但连接数很高,且大量连接处于慢速读取状态,那就重点调整超时和速率限制。
日志分析在事后溯源和策略调优中不可或缺。分析Web服务器访问日志,找出攻击者使用的User-Agent特征、请求的目标URL、来源IP的地理分布等信息。这些数据可以帮助你制定更精准的访问控制规则。比如发现攻击流量都来自某个特定网段或者使用了特定的请求头,就可以临时在防火墙或WAF上对这些特征进行封锁。但要注意,攻击者会不断变换特征,所以规则需要灵活调整。
最后,架构层面的弹性设计也很关键。把静态资源和动态请求分离,静态资源走CDN,动态请求走独立的API服务器。数据库做读写分离,缓存层前置。这样即使某一层被攻击拖垮,也不至于整个系统完全瘫痪。连接数占满往往先出现在代理层或应用服务器上,通过水平扩展这些无状态节点,可以在一定程度上吸收攻击。当然,如果攻击规模远超你的扩展能力,最终还是需要依赖专业的清洗服务。
带宽占满和连接数占满,一个是网络层的资源耗尽,一个是传输层和应用层的资源耗尽。前者需要更大的管道和上游清洗,后者需要更精细的服务器调优和访问控制。两者没有银弹式的单一解决方案,只有理解其本质差异,才能在攻击发生时做出正确的判断和响应,用最小的成本保护业务的连续性。
