1. DDoS 与 CC 攻击:先搞清楚它们到底是什么
很多人一听到“网站被打了”,第一反应就是 DDoS(分布式拒绝服务攻击)。但实际上,日常运营里遇到最多的“打”,往往是 CC 攻击这种更隐蔽、更难缠的变种。我做网站运维这些年,见过太多因为分不清这两者而误判战况的案例——防御方案完全用错,钱花了,攻击没挡住,业务照样崩。
先说结论:DDoS 和 CC 攻击的目标本质上是同一个——让合法用户访问不到你的服务。但它们的手段、攻击对象、识别特征和防御思路,差别非常大。
DDoS 攻击打的是“带宽”和“连接”。攻击者用大量被控的机器(肉鸡)向你的服务器发送海量数据包,把机房出口带宽占满,或者把服务器的连接数打满。你的服务器 CPU 可能只有 5% 的占用率,但带宽已经被塞满了,正常用户根本挤不进来。这就像一群人堵在商场门口,把门都堵死了,里面的店员其实挺闲的,但外面的顾客进不来。
CC 攻击(Challenge Collapsar,最早是针对某防火墙产品的攻击方式而得名)打的是“应用层”。攻击者模拟真实用户的浏览器行为,不断发起看起来完全正常的 HTTP 请求,比如反复请求你的登录接口、查询接口、搜索接口。每个请求单独看都没问题,但单位时间内的请求量远超正常水平,服务器需要花大量 CPU 和数据库资源去处理这些请求,最终导致应用层资源耗尽,正常用户访问时页面加载不出来或者直接超时。这就像一群人不进商场,就在门口反复按自动门的开关,把门的控制器搞死机了。
搞清楚这个区别是一切防御的前提。带宽被塞满,你得找高防 IP、加大带宽清洗;应用层被打,你得做限频、加验证码、优化业务逻辑。方案反了,怎么做都是徒劳。
这篇文章我会从实际运维的角度,把两类攻击的识别特征、防御手段和踩坑经验一次讲透,适合自己做站点、管服务器的开发者,以及刚入门的安全运维同学参考。
2. 攻击特征识别:怎么判断你的站点正在被攻击,以及是哪一种
在谈防御之前,必须先学会识别。我见过太多人把“服务器高负载”和“被 CC 攻击”混为一谈,结果排查方向全错。下面这套判断流程是我在实际处理中反复验证过的,把它当 checklist 用。
2.1 带宽型攻击的典型信号
如果你的站点突然打不开,先看两个最基础的数据:带宽占用和服务器负载。
DDoS 攻击发生时,最显著的特征是入口带宽瞬间飙升。正常情况下你的站点日均流量 100Mbps,突然变成 2Gbps,十有八九是被流量打满。你可以在机房控制台或云服务商的监控面板里查看带宽曲线,如果是一条近乎垂直的上升直线,基本可以断定是 DDoS。
此时服务器负载反而可能正常,甚至偏低。很多人不理解这点,原因是攻击流量在到达服务器之前就已经把带宽填满了,数据包根本没机会被 CPU 完整处理。用生活类比说:快递仓库的收货通道被货车堵死了,仓库里的分拣员反而闲着。判断这种攻击时,只看服务器负载会误诊,必须看带宽曲线。
另一个判断依据是流量分布。正常的业务流量无非是网页、图片、视频资源,流量曲线有高有低,有规律可循。而 DDoS 流量往往是满速恒定输出,或者呈现周期性脉冲,配合连接数同步暴涨。看到这种形态,基本可以确定是带宽型攻击。
2.2 CC 攻击的隐蔽特征:负载飙升但带宽不高
CC 攻击最让人头疼的就是“隐蔽”。攻击请求和正常用户请求在协议层面几乎一模一样,单纯看带宽看不出异常——攻击者不会傻到把带宽打满,那样反而容易被流量清洗识别。他们的目标是让每个请求都消耗服务器资源,用低带宽成本制造出高 CPU 负载。
典型特征包括这么几个:
服务器 CPU 和内存使用率持续高位。如果你发现 CPU 长时间超过 90%,但带宽只有正常水平甚至偏低,标准做法是立刻查看 Web 服务日志和数据库慢查询日志。CC 攻击的直接后果是应用层 API 或动态页面被反复请求,导致数据库连接耗尽,日志里会出现大量重复的请求地址。
单 IP 请求频率极高。正常用户一秒内请求两三页就了不起了,攻击脚本可以做到一秒几十上百次。你把 access log 按 IP 分组统计请求次数,top 的几个 IP 如果每次刷新都在增长,基本就是攻击源。这里要注意,CDN 开启后你看到的 IP 可能是 CDN 节点的地址,需要结合 CDN 日志或回源日志做二次判断,否则容易误伤 CDN 节点。
请求路径高度集中。CC 攻击通常会盯着几个特定接口打,比如登录接口、搜索接口、商品详情接口。原因很简单——这些接口基本都需要查数据库、做复杂逻辑,资源消耗大,打一个顶十个。如果你发现某个本来正常的接口请求量突然暴增几十倍,得立刻查是业务推广了,还是被 CC 了。判断方法很简单:对比请求来源、频率分布和转化率,推广流量会带来成交量,CC 流量只有消耗没有产出。
2.3 借助外部位点工具辅助判断
遇到紧急情况,来不及登录服务器看日志时,我通常会先在外网用第三方拨测工具做快速判断。从不同地域的多个节点发起 HTTP 请求,观察响应时间和成功率。如果全国多地域都超时或 5xx,说明服务已经整体不可用;如果只有部分地域异常,可能是链路问题而非攻击。同时对比移动网络和宽带网络的访问情况,能进一步缩小范围。
外部位点有个好处:它是从攻击者视角看你服务的可用性,比你自己在服务器上敲命令更接近用户的真实体验。但要注意,拨测工具本身也有局限,只能说明“服务不可用”,不能直接判定攻击类型。判定类型还是得回服务器看数据。
2.4 快速判断:是 DDoS 还是 CC
把关键特征列成一张表,方便对照排查:
| 判断维度 | DDoS 带宽型 | CC 应用型 |
|---|---|---|
| 带宽占用 | 瞬间飙高,持续在峰值 | 正常或略高 |
| 服务器负载 | 正常或偏低 | CPU、内存持续高位 |
| 数据库状态 | 基本正常 | 连接数爆满,慢查询增多 |
| 连接特征 | SYN 半连接大量堆积 | 完整 HTTP 请求大量出现 |
| 请求内容 | 数据包无意义,协议层攻击 | 看似正常的 HTTP 请求 |
| 日志特征 | 大量握手失败记录 | 特定 URL 请求次数暴增 |
| 攻击成本 | 高,需要大量带宽资源 | 低,少量主机即可造成较大影响 |
用这张表对照,大多数情况下 10 分钟内就能判断出攻击类型。如果两个特征交叉出现,比如带宽飙高且 CPU 也爆了,大概率是混合攻击——DDoS 打底,CC 跟进补刀。这几年混合攻击越来越常见,防御时不能只防一层。
3. 防御体系建设:从接入层到应用层的分层对抗
防御的核心原则是分层设防。不要指望某单一产品把所有攻击都挡住,这不现实。我正常的防御架构分四层:接入层负责流量清洗,网络层负责协议栈加固,Web 层负责应用过滤,业务层负责逻辑兜底。每一层解决一个层面的问题。
3.1 接入层:高防 IP 与流量清洗中心
面对带宽型 DDoS,普通服务器没有任何硬扛的资本。你一个 10G 带宽的服务器,攻击流量打到 20G,神仙来了也扛不住。唯一可行的方案是把流量引到具备超大带宽清洗能力的节点,让攻击流量在到达源站之前被丢弃。
高防 IP 的基本原理是 DNS 解析把流量引入高防机房,清洗设备实时分析流量特征,把攻击包丢弃,把正常流量回源到你的服务器。选择高防 IP 时务必关注三个指标:最大防护能力、清洗算法、回源带宽。防护能力要大于你日常峰值的 3 倍以上,留足余量;清洗算法最好支持 TCP 协议栈行为分析;回源带宽决定了清洗后的正常流量能否完整回到源站,这个参数常被忽略,但实际上回源带宽不够,照样会把正常用户卡在门外。
接入高防后,还需要把源站 IP 藏好。攻击者一旦通过其他渠道拿到源站 IP,可以直接绕过高防打源站,这就是所谓的“绕过防护”。常见做法是仅在防火墙放行高防节点 IP 段,其他来源全部拒绝,同时关闭源站的非业务端口,减小暴露面。
3.2 网络层:内核参数与防火墙规则加固
接入层之外,服务器自身的网络层加固也很重要。即使流量清洗帮你挡住了大头,但仍可能有少量漏网流量打到源站,如果源站自身的协议栈没有加固,这些零散的 SYN 包就能把系统搞瘫痪。
Linux 服务器上我常用这么几个内核参数调整,都是经过多轮实战验证的:
net.ipv4.tcp_syn_retries 适当减小,降低 SYN 重传次数,加快半连接清理;net.ipv4.tcp_max_syn_backlog 增大,允许更多半连接排队,配合反向代理使用效果更好;net.ipv4.tcp_fin_timeout 调低,加快 TCP 连接释放。这些参数不会是万能的,但可以显著提高系统在异常连接风暴下的存活率,属于“无成本提升抗打击能力”的基础操作。
防火墙规则方面,通常的做法是封禁 ICMP 协议,避免被用于反射放大攻击;限制单 IP 的并发连接数,用 iptables 的 connlimit 模块即可实现;DDoS 高发期主动封禁海外异常 IP 段。这层防御的价值在于“兜底”,把清洗中心漏掉的零散攻击流量挡住,给 Web 层争取处理时间。
3.3 Web 层:反向代理、限频与验证码
CC 攻击的对抗主战场在 Web 层。攻击者伪装成正常用户发 HTTP 请求,但他们的行为和真实用户有明显区别——请求频率高、不加载静态资源、不走正常交互路径。识别这些差异并加以限制,是 CC 防御的核心思路。
我用 Nginx 做 Web 层防护时,首先开启 limit_req 模块做 IP 级别的请求频率限制。以一个典型的 API 接口为例:
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s; server { location /api/ { limit_req zone=api_limit burst=20 nodelay; limit_req_status 429; proxy_pass http://backend; } }这段配置限制单个 IP 每秒最多 10 次请求,瞬时超过允许最多 20 个突发请求排队,超出部分直接返回 429。对于绝大多数 Web 应用,单个用户每秒请求 10 次是绝对够用的;如果某些场景需要更高的频率(比如轮询接口),就把阈值适当上调,但一定不能取消限制。
IP 级别的限频有天然的弱点——攻击者可以分布大量 IP 绕开。更可靠的做法是配合会话识别和验证码机制。具体操作是在 Nginx 层识别无 Cookie 的请求、无 Referer 的请求、访问频率超出人工操作极限的请求,将这些流量导向验证码页面。验证码会增加攻击成本,让对手必须接入打码平台,从而拉高攻击代价。自己开发成本高的话,直接用现成的防 CC 模块就能在 Web 层挡住大部分低端 CC。
同时开启 Nginx 访问日志的详细记录,方便事后分析攻击来源和攻击路径。没有日志的防御是盲人摸象——流量被挡了,但攻击请求长什么样、从哪来、打哪个接口,一概不知,下一次攻击来了照样抓瞎。
3.4 业务层:接口级防护与资源优化
最高效的 CC 防御不只在网络层和 Web 层,还要深入业务代码本身。很多开发者把防御思路局限在“外部拦截”,忽略了“自身强壮”这个维度。同样一个接口,处理请求的代价从 10ms 优化到 1ms,服务器能承受的攻击流量就扩大 10 倍。这是最扎实的防御。
数据库层的防护优先级最高。CC 攻击对资源的消耗,真正的大头常常在数据库。同样一个查询,如果走缓存,单次耗时可能是零点几毫秒;如果绕过缓存查库并做全表扫描,可能是几百毫秒,消耗差异上百倍。建议针对热点接口设置多级缓存,尤其是商品详情、列表页这类常见被打目标。静态资源直接上 CDN 并通过 Nginx 直接返回,不回源。
动态接口可以做参数级校验,减轻无效请求的消耗。比如一个查询接口要求必须传入合法的分页参数和必要的鉴权字段,非法请求直接返回参数错误,不进入业务逻辑。这个思路很多人都清楚但执行得慢,结果防御效果也出不来。接口的鉴权、频率和参数校验必须放在业务逻辑的最前端,而不是中间或最后。
还有一点容易忽略:异常请求的快速失败机制。比如某个接口正常情况下每天调用 1 万次,突然涨到 100 万次,业务层可以做熔断——直接拒绝后续请求,返回“服务繁忙”,保住核心链路不被打崩。这属于典型的“断臂求生”,比整个服务全部宕机强得多。
4. 实操复盘:一次典型的混合攻击处理全程
理论说多了容易飘,我拿一次真实的处置过程来复盘,把前文的知识点串起来。这起事件发生在某电商业务大促前的压测阶段,攻击者选择这个时间点,时机相当讲究。
那是一个周四下午,我正准备下班前巡检最后一轮监控。负载监控突然弹出告警:Web 服务器 CPU 从平时 15% 的基线直接拉到接近 95%。第一反应是业务代码出了问题,因为带宽监控显示正常,总流量约 800Mbps,对于这家业务体量来说不算异常。但我打开 Nginx access log 看一眼,马上意识到不对——登录接口每秒请求数超过 3000 次,正常峰值只有 200 左右,并且请求来源分布在几百个 IP 上。
确认了是 CC 攻击,我按顺序做了四件事:
第一件事:在 CDN 层临时开启全局频率限制,单 IP 每 5 秒最多 30 次请求。这一步可以挡住相当一部分协议特征明显的攻击流量,给后端的调整争取时间。效果立竿见影,登录接口的请求量迅速降到了 500 次每秒。
第二件事:登录 Nginx 服务器,把登录接口的 limit_req 阈值从正常值调整到 5r/s,并开启验证码。有些研发和产品同学提出异议,说会影响正常用户体验。我的回复是:如果服务器被 CC 打崩,所有用户都无法登录;现在只是让部分异常用户通过验证码验证身份,少数正常用户顶多多一次验证。两害相权取其轻。业务方同意了。
第三件事:分析 attack log,定位攻击请求特征。我导出了最近 10 分钟的日志,用数据分析脚本按 IP、User-Agent、Referer 分组统计。结果发现虽然源 IP 分散,但 User-Agent 高度集中,主要有两类,并且请求时间间隔呈现出机器行为的固定节奏。顺着这个线索,我直接在 CDN 层加了 User-Agent 过滤规则,把这两类 UA 的请求全部拦截。这一步操作后,攻击流量又降了一个量级。
第四件事:排查数据库连接数。CPU 降下来之后,我发现数据库连接数还是很高,慢查询里有大量重复的登录验证查询。判断是攻击流量已经把数据库连接池打穿,存在大量未释放的连接。果断重启了连接池,并调整了数据库最大连接数和查询超时阈值。重启后系统才算完全恢复。
这四步操作从发现到处理完,前后大约 40 分钟。业务实际影响是:登录接口出现约 15 分钟的响应缓慢,但未发生完全不可用。
复盘这起事件,我觉得有四个经验值得反复说:
第一,判断攻击类型不能只看单一指标。我最初差点误判成代码问题,就是只看了 CPU 而忽略了带宽对比。把带宽、CPU、连接数、日志几个维度放在一起看,判断准很多。
第二,防御操作要分优先级。先做接入层临时限制,再做后端限频,最后做精细拦截。每一步立刻生效,可以快速止血,而不是花 20 分钟写一堆精确规则,结果后台系统早就被拖垮了。
第三,业务方沟通是防御工作的一部分。限频可能会短暂影响用户体验,如果不提前说清楚理由,业务方很容易在压力下叫停防御动作。把技术判断翻译成业务语言,内部沟通顺畅了,打防御战才不会被自己人打断。
第四,攻击特征要数据化沉淀。这次用的 User-Agent 特征和请求节奏模式,我写进了应急手册并固化成规则,下次再遇到同类攻击,直接套用。
5. 常见误判与踩坑记录:这些东西没人告诉你
做防御的时间久了,你会发现绝大多数问题不是“不知道怎么防”,而是“不知道坑在哪里”。我把这几年踩过的坑、以及从同行那里收集到的常见误判整理一下,每个都是花过代价换来的。
5.1 误把推广流量当攻击
电商大促期间运营同学通常会大量投放引流广告,流量瞬间暴涨,接口请求量和成交量同步上升。如果这时候不看业务指标就急着开启限频,等于把花钱买的正常流量拒之门外。我的判断标准很简单:请求量暴增但成交量、注册量等业务指标同步上涨,十有八九是推广效果;请求量暴增但业务指标纹丝不动,才放心按攻击处置。
5.2 只加防护不压测
很多团队上线了高防 IP、加了 WAF 规则,就以为万事大吉。结果攻击真来了,高防把攻击流量挡在门外,但正常流量回源时发现源站代码性能本来就差,并发稍微上来一点就垮了。我见过不止一次——攻击被清洗了,但站点依然打不开,原因就是源站本身只能扛 100 并发,而正常高峰需要 200 并发。
防御体系上线后,一定要做压力回源测试,验证“清洗后正常流量能否扛得住”。这个测试建议每季度做一次,因为业务代码在持续变化,性能基线也在变。定期压测就像是给免疫系统做体检,不能等病毒来了才检查。
5.3 把所有防御寄托在某种单点方案上
任何单一防护方案都有缺陷。高防 IP 挡得住带宽型打,但防不住应用层的慢速 CC;WAF 能识别规则型攻击,但对大量低慢速请求(Slowloris 类型攻击)的应对能力有限;代码层面的防护能力再强,也挡不住对手把带宽打到几十 G 的纯粹资源消耗。
理想架构是前面说的四层配合。虽然多一层防御就多一份成本,但这笔钱不能省。安全建设的本质是风险管理,用可控的投入换取业务可用性的确定性。
5.4 对 UDP 反射攻击认识不足
UDP 反射放大攻击是 DDoS 的重要类别,攻击者向开放的 DNS、NTP、Memcached 等服务器发送源 IP 伪造成目标地址的小请求,服务端回复的大流量就全部打向目标。这类攻击流量峰值可以做到放大几十倍甚至上百倍,带宽型 DDoS 的“大场面”很多来自这种手段。识别特征主要是流量类型高度集中于 UDP、源端口相对固定。防御思路比较简单直接:在网络层直接禁掉业务不需要的 UDP 端口,需要用的则做 UDP 流量限速。大多数 Web 业务根本不需要对外提供 UDP 服务,禁用后这类攻击基本也就废了。
5.5 忽视日志,事后无法溯源
有些团队被攻击之后只关注“现在恢复没有”,却忽略了“攻击是怎么打的”。攻击结束后,日志里记录了完整的攻击特征,这些是宝贵的分析资料。建议每次应急结束后,导出完整日志,形成一份包含攻击类型、攻击源特征、时间线、处置动作的复盘文档。做得多了,你就会形成自己的攻击特征库,下次再过招时,可能不用登录服务器就能判断对方用的是什么套路。
6. 防御的更高层次:把安全能力做进系统设计
聊完具体的对抗手段,我想再往上拔一层。真正让站点难以被打瘫的因素,不在于单次防御有多强,而在于整体架构能不能承受局部失败。
优先把静态资源全部放 CDN。CPU 密集型的图片缩放、文件压缩预处理放到离线任务去做,不在线实时计算。数据库全部走连接池并设置合理的最大连接数,防止连接被耗尽拖垮整个实例。核心服务做多节点负载均衡,单点被攻击或宕机后,流量自动切换。这些设计在平时只是在提升性能和稳定性,在攻击场景下就是在提高生存概率。
另外要明确的一点是安全投入的均衡性。对大多数中小站点来说,花大钱买超大防护能力未必划算。攻击者盯上你的原因通常是“好打”,而不是“你很重要”。把你的站点做得比同类站点更难打,攻击者大概率就会换个目标。安全建设很像加固房门——你不需要做到银行金库级别,只要比邻居家难撬,小偷就会去撬邻居家。
我自己秉持一个基本原则:日常巡检多投入一分钟,攻击来时就能少花一个小时。把监控告警、日志采集、应急演练这些“平时用不上”的事情做实做透,关键时刻它们就是救命的东西。监控不是摆设,告警不是骚扰,它们是你在攻击发生时的眼睛和耳朵。很多团队的告警发出去没人看,或者阈值设置不合理导致告警风暴,最后运维人员对告警脱敏了,真正出事时反而没人响应。一套靠谱的告警体系,值得花时间打磨。
最后再分享一个实操经验——把“攻击场景应急预案”写下来。内容包括攻击类型判断流程、操作步骤、责任人和沟通机制。平时每月花 10 分钟演练一次预案,攻击真正发生时,团队可以像肌肉记忆一样执行,而不会乱成一团。从我的体感来说,预案演练过的团队,平均处置时间至少能缩短一半。这个投入,是你为站点安全买的最便宜也最可靠的一份保险。