我做运维这些年,最怕的不是业务代码出bug,也不是磁盘被写满,而是凌晨三点被电话叫醒,对面只说一句:“服务挂了,入口流量特别大。”这句话一出来,我心里基本就有数了——多半不是普通故障,而是DDoS攻击。今天这篇心得,就是把我这几年踩过的坑、总结的鉴别方法、应急流程、防御思路,还有那些没法写进文档的体感经验,全部整理一遍。文章不藏私,也不故作高深,适合刚接触运维的开发者,也适合被“打”过但还没梳理出反制思路的同行。
先说结论:DDoS攻击的核心是“消耗”,要么耗尽带宽,要么耗尽连接,要么耗尽CPU。防DDoS绝对不能只靠一台服务器、一条命令解决,它是一套从网络入口到应用层的链路工程。下面从一次真实误判讲起。
1. 凌晨三点的告警电话:第一次被DDoS时的错误判断
1.1 现象描述:CPU不高,但服务就是超时
那次故障发生在某个线上业务的大促前夕。凌晨3点,监控告警先响了,紧接着电话就追了过来。我登录服务器一看,很困惑:CPU负载只有20%,内存也没满,磁盘IO正常,数据库慢查询也没暴增。但用户端反馈已经非常糟糕:页面转圈、接口超时、App直接报网络异常。
我第一反应是查业务代码,怀疑某个接口出现死循环或者Redis连接池泄漏。查了Nginx访问日志,发现请求量确实涨了很多,但都是正常的GET请求,状态码大部分还是200,可响应时间从几十毫秒涨到了十几秒。这时候我又怀疑是数据库连接被打满,去查连接数,发现数据库连接数确实很高,但远没有达到配置的上限。
那会儿我犯了一个典型的错误:把“服务慢”都归因到应用层,连续排查了代码、中间件、数据库,白折腾了将近一个小时。后来带我的老前辈上线,扫了一眼入口流量监控,直接说:“你看看带宽,入向流量已经打满100Mbps了,业务平时才跑20Mbps,这不是代码问题,是流量攻击。”
1.2 我后来总结的识别清单
从那之后,凡是遇到“服务变慢、超时、连接不上”的故障,我先按这套清单排除DDoS,再往下查应用:
- 看入口带宽:对比最近7天同一时段带宽曲线,如果入向流量暴涨5倍以上甚至打满,优先怀疑流量型攻击。
- 看连接状态:
netstat -ant里如果出现大量SYN_RECV,不用犹豫,多半是SYN Flood;如果大量TIME_WAIT且来源IP分散,也可能是四层代理被压垮。 - 看CPU与带宽的组合:CPU正常、带宽异常,基本是四层流量型;CPU飙升、带宽不高且QPS暴涨,多半是应用层CC攻击。
- 看出口业务指标:成功率下降、响应时间大幅波动、部分地域完全不可用,往往意味着攻击流量打到了网络入口而不是某一台机器。
这套清单现在被我写成了团队故障排查SOP的第一页。核心思路就是先判断“攻击发生在哪一层”,再决定怎么止血。如果把DDoS误当成应用故障,轻则多折腾几小时,重则让攻击持续扩大,最终拖垮整个集群。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 剥开流量看本质:四种常见攻击形态的鉴别方法
DDoS不是一个单一的攻击,而是一大类攻击的统称。不同形态的流量特征完全不一样,对应的防御手段也完全不同。我总结下来,日常最常见的就四种:SYN Flood、UDP反射放大、HTTP层CC、慢速攻击。加上这四种经常混合使用,识别能力就显得特别重要。
2.1 SYN Flood:半连接队列被塞满
SYN Flood的原理说穿了很简单:TCP握手要三步,攻击者只发第一步的SYN包,不回应第三步的ACK,让服务器一直保持半连接状态。服务器能维护的半连接队列是有限的,队列一满,正常的客户端连接就进不来了。
鉴别它的几个特征非常明显:
netstat -ant | grep SYN_RECV | wc -l数量飞涨,正常情况下这个值应该只有个位数、最多几十。- 抓包能看到大量源IP伪造的SYN包重复到达,且源IP段分布很散。
- 服务器CPU并不一定高,但新建连接全部失败,用户表现为“连不上”。
防御上我先说最核心的:开启Linux的tcp_syncookies,它能把半连接状态编码进SYN+ACK包,绕过半连接队列的限制。配合增大tcp_max_syn_backlog和somaxconn,能在小规模攻击下撑住。但这只是宿主级的缓解,真正大规模SYN Flood还是需要在网络入口做SYN Proxy,把握手过程放到清洗设备上完成。
2.2 UDP反射放大:流量被“借刀杀人”灌进来
UDP反射放大是另一种让人很头疼的流量型攻击。攻击者把源IP伪造成受害者的IP,向互联网上大量开放的DNS、NTP、CLDAP等服务发送很小的请求包,这些服务返回的响应包却很大,而且响应目的地是受害者。攻击者用很小的带宽,就能让受害者收到几十倍甚至几百倍的流量。
我在一次防护过程中遇到过典型的DNS反射放大:业务IP被伪造源发起大量查询请求,入口带宽瞬间从100Mbps冲到几个Gbps。鉴别方法主要靠抓包和端口统计:
- 入向UDP流量占比异常高,且源端口集中在53(DNS)、123(NTP)、389(CLDAP)等。
- 源IP分散在全世界,单个源IP的流量不大,但聚合起来巨大。
- 服务器CPU可能正常,但网络入口已经完全堵死。
这种攻击单靠服务器本身没法防,因为流量到达服务器之前,带宽已经满了。正确做法是把流量交给清洗设备或高防节点,识别UDP反射特征后直接丢弃。如果业务本身不需要对外UDP服务,可以在防火墙把非业务UDP端口全部封掉,能挡掉一部分流量。
2.3 HTTP层CC攻击:伪装成正常用户的“软刀子”
CC攻击和前面两种不一样,它走的是正常的TCP连接、正常的HTTP协议,每一个请求看起来都像真人访问。攻击者用大量代理IP,模拟真实用户不断请求慢接口、搜索接口、下载接口,目的是把应用服务器的CPU、数据库连接、缓存连接全部打满。
它的特征也很典型:
- QPS看起来高,但远不如大促峰值,服务器CPU/内存却被慢慢耗干。
- 访问日志里同一个URL被高频请求,但UA、Referer、Cookie异常统一,或者缺失。
- 攻击IP分散,来源地域、运营商五花八门,很难用封IP解决。
- 业务表现为“越来越慢”,而不是瞬间不可用。
防御CC的关键是识别“人”和“机器”。我常用的手段包括:Nginx层做limit_req限速,针对关键API限制单IP每秒请求数;WAF层做人机验证,滑块、点选验证码可以挡掉大批脚本;再配合缓存,把高频读接口用CDN或Redis提前挡掉。这里有个经验:CC防御一定要分层,单靠Nginx限速挡住不了一切,单靠WAF也可能影响正常用户,要组合用。
2.4 混合攻击与慢速攻击:最考验应急能力的情况
现在攻击者很少只用单一手法。常见组合是:先用SYN Flood打网络入口,再用CC打应用层,让你顾此失彼。慢速攻击则会长时间占用连接不释放,比如发起一个HTTP请求后一直不发完,把服务器的并发连接数慢慢消耗光。
慢速攻击的特征是:服务器在线连接数持续上涨,但流量不大、CPU不高。传统的max_clients、worker_connections配置不够大时,很容易被打挂。防御思路是缩短超时时间,比如Nginx的client_header_timeout、client_body_timeout都设短一些,并在四层加连接数限制,超过阈值的IP直接丢弃。
碰到混合攻击,就不要妄想靠一招解决问题了。我当时跟团队定的策略是:入口流量异常走清洗,连接层做黑白名单和限速,应用层走WAF和验证码,三层同时上。也只有到了这个阶段,才能真正理解为什么防御DDoS不能单买一个“高防IP”就万事大吉。
| 攻击类型 | 主要特征 | 主要影响 | 首选防御手段 |
|---|---|---|---|
| SYN Flood | 大量SYN_RECV,源IP分散 | 连接无法建立 | tcp_syncookies、SYN Proxy |
| UDP反射放大 | UDP流量巨增,源端口常见53/123 | 带宽被占满 | 入口清洗、封非业务UDP端口 |
| HTTP CC | QPS较高,请求集中,UA异常 | CPU/内存耗尽 | 限速、验证码、缓存 |
| 慢速攻击 | 连接数上涨,流量不高 | 并发连接耗尽 | 缩短超时、限制连接数 |
3. 应急响应全链路:从黑洞路由到回源保护
被打的时候最怕两件事:一是不知道先做什么,二是不敢做决定。尤其是“黑洞”这个操作,很多人一听到就害怕,怕流量被丢弃后业务彻底不可用。但实际应急场景里,在带宽已经打满的情况下,黑洞往往是止损的手段。下面我把应急链路按顺序写清楚。
3.1 第一步:先保可用性,再谈反制
应急的原则只有一条:先止损,再追溯。如果业务已经整体不可用,第一要务是让流量降下来,而不是去分析攻击者是谁。
具体分两种情况:
- 如果攻击流量在业务可承受范围内,比如带宽没打满,可以先开启速率限制,继续观察。
- 如果入口带宽已经打满,网卡流量图都成了直线,那本地任何iptables规则都没用了,因为流量在到达服务器之前就已经占满线路。这时候必须联系服务商或IDC,申请黑洞或牵引流量到清洗中心。
这里我要多说一句:很多人觉得“黑洞”是把流量全丢,业务直接挂,但其实在被打满时,业务本来就已经挂了。把故障IP黑洞掉,至少能保住同机房的其它机器、其它业务不受牵连。所以平时就要跟服务商确认好黑洞阈值、黑洞时长、自动解封流程,别到被打时才去找联系方式。
3.2 命令行里的“止血”:内核参数与限流规则
在等待清洗设备介入的同时,宿主机上的临时止血也不能停。我常用的Linux内核参数组合如下,适用中小规模攻击:
bash复制# 开启SYN Cookie,抵御SYN Flood
sysctl -w net.ipv4.tcp_syncookies=1
# 增大半连接队列与全连接队列
sysctl -w net.ipv4.tcp_max_syn_backlog=65535
sysctl -w net.core.somaxconn=65535
# 缩短SYN重传次数,避免无效重试堆积
sysctl -w net.ipv4.tcp_syn_retries=1
# 增大连接跟踪表,避免高并发下连接被丢弃
sysctl -w net.netfilter.nf_conntrack_max=1048576
注意一点,很多人会习惯性开tcp_tw_recycle,这个参数在Linux 4.12之后已经被移除了,而且它有一个很坑的副作用:会让NAT后方的用户连接失败。我自己在早期踩过这个坑,开了之后一堆手机用户连不上,排查了半天。现在的内核版本里,tcp_tw_reuse可以保留,tcp_tw_recycle千万别再用了。
Nginx层也可以立刻加上限速:
nginx复制limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn perip 20;
limit_req_zone $binary_remote_addr zone=reqperip:10m rate=10r/s;
limit_req zone=reqperip burst=20 nodelay;
这段配置的意思是:单个源IP最多同时保持20个连接,每秒最多处理10个请求,超出部分直接返回503。虽然它会误伤一部分正常用户,但在被CC攻击的短时间内,启用它比整个服务挂掉要好得多。攻击结束之后要记得改回正常阈值,用动态配置或临时下发的方式管理。
3.3 与服务商/IDC协同:黑洞、清洗与流量调度
应急过程中,本地操作只是过渡,真正能扛住大流量的是上游链路。我们需要在最短时间内做三件事:
- 联系服务商开启流量清洗,也就是把指到源站IP的流量先经过清洗中心,识别并丢弃攻击流量后再回源。
- 如果业务域名已接入CDN或高防IP,立刻把解析切到高防节点,让攻击流量打到高防上而不是源站。
- 如果清洗也扛不住,根据服务商建议申请黑洞,保住整体机房的稳定。
这里有个容易忽略的细节:在接入高防或清洗后,源站的防火墙和安全组一定要设置成“只允许清洗中心/高防节点的回源IP访问”,否则攻击者发现源站IP后,仍可以绕过清洗直打源站。我在某次防御中就是因为安全组策略没改,高防IP切好了,结果源站还是被继续打,白白浪费了流量切换的时间。
3.4 攻击结束后别忘了复盘日志
攻击打完之后,很多人以为就结束了,其实复盘才是最有价值的环节。我的做法是:
- 从四层抓包文件和负载均衡的访问日志里提取攻击特征:攻击源端口、请求URL、报文长度、User-Agent、频率分布。
- 把特征整理成一份“攻击指纹表”,记录攻击类型、特征、持续时间、采用过的防御动作、效果如何。
- 基于指纹来更新防火墙规则、WAF策略和监控阈值,作为下次同类攻击的自动匹配依据。
有一次,我们就是因为复盘时发现攻击集中在某个老版本App的API,才意识到临时接口需要加白名单校验,后来下一次攻击刚起量,阈值告警就自动触发了,响应时间从小时级缩短到了分钟级。
4. 防御姿态的长期建设:隐藏源站、容量规划和监控告警
应急是防守的下限,日常建设才是上限。下面几个方向是我在实战中验证过、真的能降低“被打”概率和缩短恢复时间的。
4.1 为什么攻击者能找到你的真实IP
防御的第一步是别让攻击者知道打完哪里。很多团队明明买了高防,结果源站IP一泄露,高防成了摆设。源站泄露的常见途径包括:
- 域名解析直接指向源站IP,没有套CDN或高防,一查A记录就暴露了。
- 子域名太多,某个老子域名解析指向源站,攻击者通过子域名枚举找到入口。
- 邮件服务器发送的邮件头里携带原始IP,或者SSL证书的日志里暴露了站点真实地址。
- 历史DNS记录被第三方网站保存,溯源查询就能看到源站IP。
我建议的隐藏方式很直接:业务域名全量接入CDN或高防,源站IP只在安全组里放行CDN回源IP段;不要用源站直接发邮件,单独走邮件中继;老域名、测试域名全部下线或统一收敛;定期通过在线工具查自己的历史DNS记录,发现泄露及时更换源站IP。
4.2 容量规划与冗余设计
如果攻击流量超不过业务带宽冗余,防御就成功了一半。容量规划不是要求你买几百G带宽,那成本太高了,而是要求你在成本和风险之间找一个可接受的位置。
我见过一个比较健康的做法:正常业务带宽峰值为50Mbps,机房出口带宽买到了200Mbps,CDN兜底再扛几百G的清洗能力。平时流量只有50Mbps,但这多出来的150Mbps冗余,足以扛住中小规模的攻击而不影响业务。越是重要业务,越要在核心链路上做冗余,比如双机房、多线路、异地容灾,单点被黑洞时能整体切换。
4.3 监控告警要告到人,而不是告到群里
监控这里我吃过亏。以前我们的告警是发到工作群,结果半夜被刷屏,反而没人响应。后来改成“三级告警”机制:
- 带宽、PPS、SYN_RECV数量触发严重阈值时,直接电话打到值班人手机。
- 应用层QPS异常、响应时间升高时,发到值班群,并@值班人。
- 普通阈值变化只记录,不打扰。
告警阈值要基于你自己的业务基线来设定,不能拍脑袋。举个例子,业务平时SYN_RECV不超过50,你可以把严重阈值设在200;但如果业务是大促状态,直接沿用日常阈值会误报频发。所以监控系统里一定要区分“业务高峰态”和“日常态”,大促前临时调高阈值,平时保持敏感。
5. 一些由实战沉淀出来的个人心得
5.1 防御是成本取舍,而不是技术炫技
做防御决策的时候,最忌讳的是“技术至上”。我见过不少团队,为了省一点CDN费用,坚持自建高防,结果一次大流量攻击就能把年度预算全打穿。反观一些务实的团队,把高防交给服务商,源站做好安全组,日常做好监控,整体效果反而更好。
DDoS防御本质上是一个商业决策:业务重要性决定投入,投入决定冗余,冗余决定攻击下的存活率。比如一个几十个用户的内部系统,没有必要上高防;而一个面向C端、收入依赖线上的产品,就必须把防御当基础设施来预算。想清楚这一点,后面所有的技术方案都会清晰很多。
5.2 最后再分享一个小技巧:提前写好SOP,每年做一次攻防演练
我强烈建议把应急流程写成一份可执行SOP。里面至少包含:服务商联系方式与工单模板、防火墙和设备厂商线路的切换步骤、常用抓包和限流命令、黑洞阈值与解封流程、复盘模板。这份SOP不是写给老板看的,是写给凌晨三点被电话叫醒的自己看的。人处于慌乱状态下,照着SOP一步步执行,比临时回忆命令要可靠得多。
另外每半年找时间做一次小规模攻防演练,找云上靶场或者自建几台测试机,模拟压垮一个非核心测试站点。演练不需要完全复现真实攻击,重点是把“谁能做什么、先做什么、怎么通知、怎么恢复”这套流程跑顺。第一次演练肯定会手忙脚乱,多跑两次就好了。我最大的体会是:真正让你在DDoS面前站得住的,不是某个神奇的防御设备,而是你对自己业务链路每分钟的反应速度有多快。平时多准备一点,被打的时候就少慌一点。
