凌晨两点十七分,值班手机连环响。爬起来打开监控面板,一台业务机器的入向流量在四分钟里从 30Mbps 拉到 8Gbps,TCP 连接数从三位数跳到六位数,业务接口成功率从 99.9% 掉到 12%,用户端开始大面积报超时。这不是代码 bug,也不是数据库锁表,这是最典型的 DDoS 攻击现场。第一次碰上这种情况的人,脑子里蹦出来的第一个问题基本都是同一句:这玩意儿为什么防不住?
先说清楚这篇文章讲什么。DDoS(分布式拒绝服务)攻击的本质是一场资源消耗战,它不偷数据、不搞破坏,只做一件事——把你的带宽、连接表、CPU、数据库连接池这些有限资源全部占满,让你自己瘫掉。这篇内容会从成本账本、攻击路径、防护链断点、应用层硬骨头、纵深防御落地、真实复盘几个角度拆开讲,回答"为什么无解"这个问题到底成不成立,以及在这个前提下,一个普通技术团队能做到什么程度。适合运维、后端开发、安全岗和技术负责人看,也适合被问过"我们买高防了为什么还被打穿"的人看。
先把结论摆出来:DDoS 不是"技术上完全无解",而是在多数业务的成本约束下无解。这两句话差得很远,想明白这个差别,后面的防护策略才不会走偏。
1. "无解"说的不是技术,是攻防两边的成本账本对不上
很多人把"无解"理解成"没有技术手段",这是个误解。真正让人绝望的地方在于:技术手段是有的,但你付不起那个代价,或者付了代价仍然打不过对方的成本优势。这一节把这个账本算清楚。
1.1 攻击者的边际成本近乎为零,防御者的成本是线性增长
攻击者手里的弹药是僵尸网络,也就是一堆被控制的设备——家里的摄像头、路由器、老旧服务器、被塞了木马的 PC。这些设备的"租金"低得离谱,有些黑产按小时计费,几百块钱就能租到足以打出几十 Gbps 的规模。更关键的是,攻击者打你 100G 和打你 10G,成本差距可能只有几倍,但他给你造成的损失差距可能是几十倍。
防御这一侧完全是另一个模型。你要扛住 10G 攻击,得买 10G 以上的带宽和清洗能力;要扛住 100G,就得买 100G。防护能力几乎是线性堆钱堆出来的,而且这笔钱是常年支出,攻击者却只需要在你最难受的那几天集中火力。一个人天天买保险,一个人偶尔来砸场子,谁的成本结构更舒服,一目了然。
我见过一个很小的业务,日常峰值带宽 200Mbps,被打了三次,每次 20 到 40Gbps,持续二十分钟。第三次之后团队算了一笔账:如果按峰值买常年高防,一年的费用比他们整个技术团队的工资还高;如果不买,每次被打的两小时业务损失大概几万块。最后他们的选择是"买基础防护 + 接受偶发损失 + 做业务降级"。这不是技术不行,是账算下来只能这么办。
1.2 被压垮的是三种完全不同的资源,堵住一种不代表安全
新手最容易犯的错,是把 DDoS 当成一个整体去防。实际上它至少压三种资源,每一种的应对手段都不一样:
| 被压垮的资源 | 典型表现 | 主要攻击形态 | 有效应对方向 |
|---|---|---|---|
| 出口带宽 / 链路 | 整条链路堵死,所有服务一起失联 | 流量型洪泛 | 上游清洗、流量牵引、 Anycast 分散 |
| 连接表 / 会话资源 | 新连接建不上,老连接卡死 | 协议型攻击 | 内核参数调优、连接数限制、四层清洗 |
| 应用层算力 / 数据库 | CPU 打满、慢查询暴涨、连接池耗尽 | 应用层攻击 | 缓存、限流、人机识别、业务降级 |
打个比方:你的业务是一栋写字楼。带宽是楼前那条马路,连接表是大堂的访客登记台,应用算力是楼里办公的人。流量型攻击是把马路堵死,车根本开不到楼下;协议型攻击是雇一万个人在大堂排队登记,真访客挤不进去;应用层攻击更阴,来的人全都手续齐全、衣着正常,进门之后每个人占着工位问一堆复杂问题,把办公的人累死。
你加固了大堂,马路该堵还是堵;你在路口设了卡,大堂照样被排队的人塞满。所以"我上了 WAF 为什么还被打了"这类问题,答案往往是你防的是第三层,人家打的是第一层。
1.3 流量被清洗干净,业务照样可能雪崩
这一点最容易被忽略。很多人以为只要流量被清洗掉、告警解除了,事情就过去了。实际上攻击期间业务侧已经发生了连锁反应:连接池被占满导致健康检查失败、实例被负载均衡摘除、自动扩容拉起一批新机器又把数据库连接数打爆、缓存集中失效引发击穿、消息队列堆积几百万条导致消费延迟。等攻击停了,这些次生灾害还在继续。
我经历过一次,攻击只持续了十二分钟,流量峰值也就 6Gbps,清洗很快生效。但业务恢复了四十多分钟,原因是自动扩容策略在攻击期间拉起了三十台实例,每台实例启动时都要预热缓存,三十台同时去查数据库,直接把主库打到了 100% CPU。攻击是外因,真正让业务躺下的是自己的弹性策略。
防御 DDoS 的目标不是"流量归零",而是"业务可用"。这两件事在很多时候不是一回事,别把监控指标盯错了地方。
2. 从灌满管道到累死进程:四条主要攻击路径的底层逻辑
想防住,先得知道对面从哪几个方向下手。这一节不讲具体工具和命令,只讲原理——因为你需要的是判断"我现在被打的是哪一类",而不是复现攻击。
2.1 流量型洪泛:让数据包根本到不了你的网卡
流量型攻击的逻辑简单粗暴:制造海量数据包,把你的入口带宽占满。它甚至不需要完成任何协议握手,发出去就行,包的内容是什么都不重要。常见形态是用大量小包打满 PPS(每秒包数),因为很多网络设备处理小包的能力远低于处理大包——同样是 10Gbps,用 64 字节的小包打,PPS 能到一千多万,远超很多防火墙和负载均衡设备的处理上限。
这类攻击最难缠的地方在于,流量在到达你的服务器之前就已经把链路堵死了。你在服务器上装什么安全软件都没用,因为包压根进不来,或者进来的时候上游链路已经拥塞到丢包率 80%。这也是为什么流量型攻击必须在上游解决——运营商的清洗中心、云服务商的流量牵引、Anycast 把流量分散到多个节点,这些是唯一有效的方向。
有个细节值得注意:流量型攻击经常被用来打"声东击西"。先用大流量把你的运维团队吸引到带宽监控上,真正的应用层攻击在同一时间打你的支付接口。我在一次事件里就吃过这个亏,前十分钟所有人都在看流量图,等发现支付成功率掉到个位数的时候,应用的限流早就被打穿了。
2.2 协议型攻击:用半开连接把会话表塞满
TCP 建连接要三次握手。客户端发 SYN,服务端回 SYN-ACK 并把这个连接放进一个叫半连接队列的地方等着,收到客户端的 ACK 才算建好。协议型攻击就是疯狂发 SYN 但永远不回最后一个 ACK,让服务端的半连接队列被占满,后续正常用户的 SYN 全部被丢掉。
现代操作系统内核早就有应对手段,比如 SYN Cookie,在队列满的时候不回 SYN-ACK 而是回一个带加密信息的序号,只有客户端正确回应才分配资源。这让纯粹的 SYN Flood 效果大打折扣。但攻击手法也在演进:完整的 TCP 握手但之后不发数据、慢速发送 header、长时间占用连接不关闭,这些都能绕过 Cookie 机制,因为连接确实"建好了"。
这类攻击的核心是消耗连接数这个稀缺资源。一台服务器的文件描述符是有限的,Nginx 的 worker_connections 是有限的,负载均衡的并发连接表也是有限的。几十万个连接挂在那里不动,正常用户的连接就建不起来。应对这类攻击,内核参数调优只能缓解,真正的解法是在四层做连接数限制和异常连接识别。
2.3 应用层攻击:每一个请求都长得像真人
这是最难的一类,也是很多团队真正被打穿的地方。应用层攻击不走大流量路线,它可能只有每秒几千个请求,带宽占用微不足道,但每一个请求都是完整的 TCP 连接、合法的 HTTP 报文、正常的 User-Agent,请求的还是有实际业务逻辑的接口——比如登录、搜索、下单。
为什么难?因为从协议层面看,它和正常用户没有区别。服务器没法靠"包长得像不像攻击"来判断,只能靠频率、行为模式、来源分布这些间接特征。而攻击者恰恰可以控制频率,每个 IP 每秒只发一两个请求,用几万个 IP 分散,总量就很可观了。这就是所谓的 CC 攻击(Challenge Collapsar),专门打消耗资源大的动态接口。
举个具体的例子。一个搜索接口单次查询要跑 200 毫秒的数据库模糊匹配,正常 QPS 是 50。攻击者用两万个 IP,每个 IP 每 10 秒发一个请求,总 QPS 就是 2000,是正常值的 40 倍。从单 IP 维度看,每 10 秒一个请求简直像个正常用户;从总量看,数据库瞬间被压垮。这种情况下按 IP 限流完全失效。
2.4 反射放大:把第三方的带宽变成打你的弹药
反射放大攻击是这几年的主流。原理是攻击者伪造源 IP,把受害者的 IP 写进请求包的源地址字段,然后把这个请求发给互联网上大量"无辜的第三方服务器"。第三方服务器收到请求后,把响应发回给受害者——而这个响应往往比请求大几十倍甚至几百倍。
这个手法的精髓在于放大倍数和第三方背锅。攻击者自己只需要很小的带宽,就能撬动几十倍的流量;而且这些流量来自全世界正常的服务器,你封也不是、不封也不是。要根治这类问题,得靠互联网整体的源地址校验(出口过滤),也就是运营商侧禁止伪造源 IP 的包出去。但这件事在全球范围内推进得很慢,所以反射放大至今仍然好用。
从防御者角度,能做的有限:识别异常的大响应流量模式、在清洗设备上配置对应协议的过滤规则、把服务藏在 CDN 后面避免源站暴露。指望彻底消灭这类攻击不现实。
3. 为什么上了高防还是被打穿:防护链条上的五个真实断点
"我们买了高防""我们接了 CDN""我们上了 WAF",这三句话我在事故复盘会上听过太多次。问题不在于这些手段没用,而在于防护是一条链,链上有任何一个断点,整条链就等于没做。这一节讲几个最常见的断点。
3.1 源站 IP 是怎么漏出去的,很多人根本没查过
所有基于云清洗的防护,成立的前提只有一个:攻击者不知道你的真实源站 IP。一旦源站暴露,攻击者直接绕过清洗中心打源站,你买的所有防护瞬间归零。
源站 IP 泄露的渠道多到超乎想象:历史 DNS 解析记录(很多第三方 DNS 历史查询服务会保留几年前 A 记录)、同一域名下的邮件服务子域名、测试环境域名、开发人员本地 hosts 配置、证书透明日志里暴露的子域、第三方支付或短信服务的回调白名单、甚至是你自己发的一封邮件里的图片外链。
我给一个检查清单,是可以直接拿去用的:
- 把所有子域名枚举一遍,逐个解析看是否指向源站 IP,尤其是 mail、test、dev、old、api 这类前缀
- 查证书透明日志,看有没有为内部域名签发过公开证书
- 检查源站服务器是否还开着 22、3306、6379 这些管理端口对外
- 检查源站是否在攻击期间被扫到过(流量日志里有没有来自陌生 IP 的直接访问)
- 换源站 IP 之后,确认所有 DNS 记录、第三方回调地址、监控探针都同步更新了
最后一条是最容易翻车的。我见过一个团队换了源站 IP,结果忘记改一个合作方的回调白名单,导致业务失败了两小时,查了半天才想起来。
3.2 清洗中心的阈值是把双刃剑:设高了漏杀,设低了误杀
清洗设备的工作方式是识别 + 过滤,而识别是有阈值的。阈值设得松,低速攻击直接漏过去;阈值设得紧,正常用户的流量被误伤。这个平衡点非常难找,因为不同业务、不同时段的正常流量特征完全不同。
举个典型场景:某业务凌晨的正常流量只有白天的 5%,攻击者在凌晨打一个 10 倍于白天流量的攻击。从绝对值看不算大,从相对值看已经是异常。如果清洗策略是按绝对值阈值配的,这个攻击会完全漏过去;如果按动态基线配,又容易在业务促销、活动期间误判。
更麻烦的是误杀带来的次生影响。有一次清洗策略过于激进,把某个地区的正常用户流量当攻击流量丢了,结果客服接到几百个投诉,业务方以为是自己代码出问题,排查了两小时才发现是防护策略。这种事在事故复盘里经常被算到"防护配置不当",但它本质上是信息不对称——安全团队不知道业务的地域分布和流量模型。
一个实用建议:把业务的正常流量画像(按时段、按地域、按接口、按 UA 分布的基线)整理成文档,交给负责防护配置的人。这件事做一次能用很久,比每次出事现查强得多。
3.3 回源链路才是真正的瓶颈,很多团队压根没算过
清洗中心的架构是:流量先进清洗节点,洗干净之后再回源到你的服务器。这里有个隐藏问题——回源带宽。
假设你的业务日常峰值 500Mbps,攻击流量 50Gbps,清洗中心把恶意流量过滤掉 99%,剩下的正常流量加漏网的攻击流量可能是 2Gbps 回源到你的服务器。而你的服务器出口带宽可能只有 1Gbps。结果就是攻击被"清洗"了,但回源链路上仍然拥塞,业务照样受影响。
还有些清洗方案默认回源走公网,回源流量不加密、不做二次校验,攻击者如果能猜到回源路径,可以在回源链路上做手脚。这部分细节各家方案差异很大,采购的时候一定要问清楚:回源带宽上限多少、回源是否走专线、回源 IP 是否固定、回源是否有二次过滤。
3.4 弹性计费下的账单型攻击:不打垮你,打穷你
这年头还有一种打法,目标不是让你的服务挂掉,而是让你的账单爆炸。很多云服务是按流量计费的,攻击者打出几百 TB 的流量,全部走你的账单。攻击持续几小时,业务可能一点感觉都没有,月底账单来了能让人当场心梗。
这类攻击在业务上完全没有可观测的异常——CPU 正常、延迟正常、错误率正常。只有流量图和账单在涨。要防这个,必须在架构层面做:设置流量封顶告警、配置费用预算告警、对按量计费资源设置上限、和云服务商确认异常流量的费用处理政策。这些事情平时不做,出事的时候是来不及的。
3.5 业务依赖的第三方被打,你也跟着挂
还有一类完全无解的连带故障:你的业务依赖的短信服务、支付网关、对象存储、DNS 服务商被打,你的服务本身没问题,但所有依赖它们的接口全部超时。这种时候你能做的只有降级。
所以架构上要留后手:短信发不出去时能不能用备用通道、支付网关挂了能不能降级为排队稍后处理、DNS 服务商挂了有没有备用解析、对象存储不可用能不能先落本地。这些设计平时看不出价值,出事的时候能救命。
4. 应用层这块硬骨头:CC 攻击为什么最难缠
前面讲的几类,多少还有明确的应对方向。应用层攻击不一样,它的核心矛盾是你无法在不伤害正常用户的前提下,把恶意请求区分出来。这一节专门讲这块。
4.1 合法与恶意的边界,本质上是一道概率题
先说一个残酷的事实:没有任何方案能 100% 区分正常请求和 CC 攻击请求。因为从单次请求的视角看,它们可能完全一样——相同的 URL、相同的参数格式、相同的 Header。区别只在统计特征上:频率、分布、时间模式、行为序列。
这就意味着所有应用层防护方案都是在做概率判断,都会有误杀和漏杀。你要做的不是追求零误杀,而是把误杀的代价控制在业务能接受的范围内。
判断一个请求是否可疑,常用的信号维度有这么几类:
- 频率维度:单 IP、单设备指纹、单账号的请求速率,以及相对于全局的偏离程度
- 分布维度:来源 IP 的地理分布、AS 分布、IP 段聚集度,攻击流量往往集中在少数几个段
- 行为维度:访问路径序列是否符合正常用户习惯(正常用户会加载页面、请求静态资源、点击按钮;脚本往往直接怼接口)
- 协议维度:Header 顺序、TLS 指纹、HTTP/2 的帧特征,成熟的攻击工具在这些细节上会和真实浏览器有差异
- 账号维度:新注册账号占比、登录失败率、同一设备多账号
单看任何一个维度都能被绕过,组合起来判断才有意义。这也是为什么纯规则的 WAF 对抗不了成熟的 CC 攻击——规则是静态的,攻击者会针对性调整。
4.2 人机识别:验证码只是最粗暴的一层
人机识别是应用层防护最有效的手段之一,但很多人对它的理解只停留在"弹验证码"。实际上它有好几个层次,代价和效果完全不同:
| 手段 | 对正常用户的打扰 | 对抗效果 | 适用场景 |
|---|---|---|---|
| JS 挑战(下发脚本计算后再放行) | 几乎无感 | 拦住简单脚本 | 全站默认开启 |
| 浏览器指纹校验 | 无感 | 拦住不带完整浏览器环境的工具 | 关键接口 |
| 滑块 / 点选验证码 | 明显 | 拦住大部分自动化 | 登录、注册、下单 |
| 短信 / 邮箱二次验证 | 很大 | 很强,但有成本 | 高风险操作 |
| 行为轨迹分析 | 无感 | 对模拟真人行为有一定效果 | 需要长期数据积累 |
实践里的顺序应该是:先上 JS 挑战做基础过滤,把最low的脚本挡掉;再对关键接口做指纹校验;最后才在异常时弹验证码。一上来就弹验证码,正常用户的体验会崩,而且攻击者用打码平台也能过。
有个细节值得说:JS 挑战的效果取决于你的挑战脚本有多"难算"。如果只是简单的一次计算,攻击者写个解析器就过了;如果涉及浏览器环境的多重校验(navigator 属性、canvas 指纹、WebGL 特征),成本会高很多。但这条路是军备竞赛,没有终点。
4.3 分层限流的具体设计:维度选错了,限流就是摆设
限流是对抗应用层攻击的基本功,但很多团队的限流配得形同虚设,原因是维度选错了。只按 IP 限流,攻击者用代理池就能绕过;只按接口总量限流,正常业务高峰就会被误伤。
正确的做法是多维度组合,从粗到细分层。下面是一份可以直接参考的 Nginx 配置思路:
# 按 IP 维度,适合对抗集中来源的攻击 limit_req_zone $binary_remote_addr zone=per_ip:20m rate=20r/s; # 按用户维度,适合对抗同一账号的刷接口 limit_req_zone $http_authorization zone=per_user:20m rate=50r/s; # 按接口维度做全局保护,防止单接口被打穿拖垮整个服务 limit_req_zone $uri zone=per_uri:10m rate=500r/s; server { location /api/search { # burst 是允许的突发量,nodelay 表示突发不排队直接放行 limit_req zone=per_ip burst=40 nodelay; limit_req zone=per_user burst=100 nodelay; limit_req zone=per_uri burst=1000 nodelay; limit_req_status 429; proxy_pass http://backend; } }几个参数的经验值:rate 设为正常峰值的 2 到 3 倍,burst 设为 rate 的两倍左右,这样正常用户的突发请求不会被打回,而持续高频的请求会被限制。limit_req_status 建议用 429 而不是 503,前者语义更准确,也方便前端做友好提示。
除了 Nginx 层,业务层还要有更细的控制。比如按账号维度限制"每分钟最多发起 5 次下单"、按设备指纹限制"每小时最多注册 3 个账号"、对消耗资源大的接口做并发数限制。这些业务规则的抗攻击能力远强于基础设施层的限流,因为攻击者要绕过它必须付出真实成本。
一个我踩过的坑:早期只在前端网关做了限流,后端服务之间是直接调用的,攻击者找到一个内部接口暴露在公网,绕过了网关限流,直接把数据库打爆。限流要做在每一个可能被访问到的入口上,包括那些你以为"只有内部才调"的接口。
4.4 把成本挪走:缓存和异步化是最省钱的一招
对抗 CC 攻击最有效的思路,其实不是"识别坏人",而是"让每个请求变便宜"。如果一个接口的响应可以从缓存直接返回,那打它一万次和打一次的成本差不多,攻击者就白费力气了。
具体做法有这么几条,按性价比排序:
- 静态资源全部上 CDN,不要让任何图片、JS、CSS 请求打到源站。这条看起来是常识,但我见过不少项目把用户头像直接从应用服务器返回。
- 热点数据加多级缓存,本地缓存 + 分布式缓存,把数据库查询挡在第二层之后。首页、榜单、商品详情这类高访问低变化的接口必须缓存。
- 接口结果缓存,对相同参数的查询做短时间缓存(比如 5 秒),能挡掉大量重复请求。
- 写操作异步化,下单、评论、点赞这类操作先进队列再处理,前端返回"处理中"。这样即使被打,压力会堆在队列而不是直接打死数据库,你可以控制消费速率。
- 降级开关提前做好,出事时能一键关掉非核心功能,把资源集中在核心链路上。
这几条做完,同样一次 CC 攻击对业务的影响可能下降一个数量级。而且这些优化对日常性能也有好处,不是纯粹的安全投入。
5. 一套能落地的纵深防御方案,以及每一步为什么这么做
讲了一堆原理,落到执行上得有具体动作。这一节给一套分层的方案,适合中小团队参考,大团队可以在此基础上扩展。
5.1 第一步永远是资产梳理和暴露面收敛
防护的前提是知道自己有什么。这一步做不扎实,后面所有配置都是空中楼阁。
具体要做的事:把所有对公网提供服务的域名、IP、端口列出来,标注每个资产的业务重要性、是否有防护、负责人是谁;把所有子域名扫一遍,找出那些被遗忘的测试环境、旧版本接口、管理后台;把服务器上对外开放的端口过一遍,关掉所有非必要端口。
这一步最大的价值在于发现那些没人管的资产。我见过太多事故是从一个两年前上线、负责人已经离职、还挂在公网上的接口开始的。攻击者不需要找最坚固的墙,只需要找没人看守的门。
建议把这份资产清单做成动态维护的表格,每次上线新服务都要登记,每季度核对一次。这件事很枯燥,但它是所有防护工作的基础。
5.2 第二步是把清洗能力放到攻击来的方向上
分层防御的核心思想是:在离攻击源最近的地方挡掉尽可能多的流量,让打到源站的流量越少越好。
一个典型的分层是这样的:
| 层级 | 作用 | 应对的攻击类型 | 关键配置 |
|---|---|---|---|
| DNS 层 | 隐藏源站、分散解析 | 全部 | 使用支持防护的解析服务、开启解析备份 |
| CDN 层 | 缓存静态资源、吸收流量 | 流量型、部分应用层 | 全站接入、源站 IP 隐藏、回源鉴权 |
| 清洗中心 | 大流量清洗过滤 | 流量型、协议型 | 清洗阈值、回源带宽、告警联动 |
| WAF | 应用层规则过滤 | 应用层、CC | 规则集更新、自定义策略、人机识别 |
| 应用层 | 限流、降级、缓存 | 应用层 | 多维度限流、降级开关、热点缓存 |
| 数据层 | 连接池隔离、读写分离 | 次生灾害 | 连接数上限、慢查询熔断 |
每一层都有它的作用边界,指望某一层解决所有问题必然失败。CDN 挡不住打接口的 CC,WAF 挡不住 50G 的流量洪泛,清洗中心挡不住业务逻辑层的刷单。这套分层的意思不是"我全都要买",而是"我知道每一层防什么,缺哪层补哪层"。
对小团队来说,务实的做法是:静态资源全部走 CDN,动态接口前面挂一个带基础防护的网关,源站 IP 严格保密,应用层做好限流和缓存。这四件事做到位,能挡掉 80% 的常见攻击。
5.3 第三步是把监控和应急做成可执行的流程
攻击来的时候最怕的不是没手段,而是不知道该用哪个手段、该找谁。所有防护配置都必须在事前变成可执行的流程。
监控上要盯的指标至少包括:入向带宽和 PPS 的突增、新建连接数异常、单 IP 和单接口的请求速率 TOP 榜、接口成功率与 P95 延迟、数据库连接数和慢查询数、队列堆积量。这些指标要能在一个面板上看到,别等到出事再一个个翻。
应急流程要写成文档,具体到"谁在什么条件下做什么操作"。举几个必须写清楚的条目:什么情况下联系清洗服务商、联系人是谁、电话多少;什么情况下开启 WAF 的严格模式;哪些功能可以降级、降级开关在哪个后台;源站 IP 需要切换时的操作步骤和验证方法。
这份文档要打印出来贴墙,或者至少放在不依赖内网的位置。我见过最尴尬的情况是:攻击把内网打挂了,应急文档存在内网 wiki 里,打不开。
5.4 第四步是定期演练,别等真打的时候才发现配置是错的
演练不等于真打,可以从低成本的开始:每季度做一次限流开关的验证,确认限流规则生效且不会误伤;每半年做一次小规模压测,看系统的真实承载上限在哪;每年做一次降级演练,随机挑一个非核心功能关掉,看监控和告警是否正常。
更进一步可以做"红蓝对抗"式的演练,让内部团队模拟攻击者的思路去找暴露面。这件事的效果远超看文档——你以为藏得很好的测试环境,别人十分钟就能扫出来。
演练还有一个隐性价值:让团队在压力下熟悉流程。真出事的时候,人的反应速度取决于之前练过多少次,而不是文档写得多详细。
6. 一次被打穿的完整排查链路复盘
讲理论容易,实操才见真章。下面复盘一个虚构的案例(团队代号用"某业务组"),完整走一遍从告警到定位到处置的过程,重点是排查的思路顺序,这个比结论更有价值。
6.1 从告警到定位:先判断打的是哪一层
凌晨一点四十,某业务组收到告警:核心接口成功率从 99.8% 掉到 40%,同时入向流量从日常的 200Mbps 涨到 3Gbps。值班同学的第一反应是查应用日志,看到大量请求超时,怀疑是数据库问题。
这个反应是常见误区。正确的第一动作应该是看流量形态,判断攻击类型,而不是扎进应用日志。
当时他们做对的几件事:
第一,看流量曲线。3Gbps 相对日常 200Mbps 是 15 倍,但远没到链路打满的程度(他们的入口是 10Gbps),所以不是纯流量洪泛。第二,看 PPS。PPS 涨幅和带宽涨幅不成比例,说明是小包为主,符合 SYN Flood 特征。第三,看新建连接数与活跃连接数的比值。新建连接暴涨但活跃连接数没怎么涨,进一步确认是大量连接建不起来或者建起来就挂。
三步下来,十五分钟内定位为协议型攻击为主,应用层可能伴随。这个判断直接决定了处置方向:不需要联系清洗中心做大流量牵引,重点是在四层做连接控制和源 IP 识别。
6.2 处置过程:先止血,再治病
定位清楚之后,处置动作按优先级排:
止血动作(十分钟内):在边界设备上开启 SYN Cookie 和连接数限制,对单 IP 新建连接速率做硬限制,把非核心业务的端口临时下线释放资源。这三个动作做完,核心接口成功率从 40% 回升到 85%。
进一步定位(半小时内):抓取攻击流量的源 IP 分布,发现高度集中在三个 C 段,且这些 IP 有一个共同特征——都访问了同一个两年前上线的旧接口。这个接口一直没有下线,也没做任何限流。
根因处理(一小时内):下线旧接口,对这三个 C 段做临时封禁,同时把这个接口的历史访问日志翻出来,发现过去一周就有零星异常访问,说明攻击者在做前期探测,只是没人注意到。
这里有个关键经验:攻击很少是毫无征兆的突然发生。大部分攻击前都有探测阶段——扫描端口、试探接口、小流量测试。如果监控能抓到这些信号,你就有提前量。他们的监控没抓到,是因为只配了成功率告警,没配"异常访问模式"告警。
6.3 事后加固:把这次的教训变成下次的防线
复盘之后他们做了几件事,我认为每一件都值得抄:
第一,把所有公网接口做了一次全面盘点,下线了 17 个无人维护的旧接口。这件事的成本很低,收益极高,因为每一个旧接口都是潜在入口。
第二,把限流从"只在前端网关"扩展到所有入口,包括那些"只有内部调用"的接口。因为内部接口一旦暴露在公网,攻击者不会管你是不是内部用的。
第三,补了异常访问模式的监控:单 IP 对同一接口的高频访问、异常 UA、异常时间段的访问量突增、新出现的 IP 段集中访问。这几条规则误报率不高,但能提早发现问题。
第四,把应急流程从"存在 wiki 里"改成"打印贴在工位旁边",并且做了一次桌面演练,确保每个人都知道找谁、按哪个开关。
第五,和业务方一起梳理了降级方案,明确了哪些功能在极端情况下可以关闭。这个方案后来在一次更大规模的攻击里发挥了作用,业务只损失了非核心功能,核心交易链路没受影响。
回头看不难发现,这次事件里真正致命的不是攻击强度,而是那个没人管的旧接口。很多"被打穿"的故事,本质上是资产管理的失败,而不是防护技术的失败。
7. 一些我自己的判断
聊了这么多,最后说几个我个人在实际工作里形成的看法,不一定对,但都是从坑里爬出来的。
第一个看法:不要把 DDoS 防御当成一个采购行为。买高防、买 WAF、买 CDN 都只是买了一层能力,能力能不能转化成防护效果,取决于你的架构设计、监控覆盖、应急流程。我见过买了全套防护仍然被打穿的团队,也见过只用基础手段但架构设计得很抗打的团队。
第二个看法:成本意识要贯穿始终。每次讨论防护方案,我都会先问一句"这个业务值多少钱"。一个月流水几万块的小业务,去堆一年几十万的防护,这个决策本身就是错的。合理的做法是接受一定风险,把资源投在能提升整体可用性的地方——比如缓存、降级、监控,这些的性价比远高于单纯堆防护带宽。
第三个看法:应用层的功夫在日常。把接口做便宜、把缓存做扎实、把降级开关做好,这些事平时看着是性能优化,出事的时候就是防护能力。相反,如果应用本身就慢、就脆弱,再强的防护也救不回来。
第四个看法:接受"打不赢所有攻击"这个事实。安全工作的目标不是消灭风险,而是把风险控制在可接受范围内。想清楚哪些攻击必须扛住(比如核心交易链路)、哪些可以妥协(比如非核心的统计接口可以暂时关掉),比追求"全面防护"务实得多。
最后一个:演练的价值高于文档。文档写得再详细,没练过的人在压力下还是会懵。哪怕只是每季度花两小时做一次桌面推演,效果也远超看十遍应急预案。