服务器突然完全连不上,用户投诉电话打爆,打开监控一看,带宽被占满,每秒请求数飙到几十万。这种情况十有八九是遭遇了DDoS攻击。第一反应肯定是切到高防IP或者联系机房做流量清洗,先把业务恢复再说。但如果你以为攻击停止就万事大吉,那下次攻击者还会用同样的方式打到你崩溃。真正要做的,是在攻击发生时和结束后,完成溯源工作,然后系统性修复架构漏洞,让攻击成本变得极高,高到对方不愿意再打你。

攻击发生时就要开始抓取证数据

很多人有个误区,觉得攻击期间系统负载太高,任何监控工具都是累赘,先把攻击扛过去再说。结果攻击一停,什么痕迹都没留下,连对方用的什么攻击方式都说不清楚。正确的做法是,在启动流量清洗的同时,立刻在服务器上部署轻量级抓包。tcpdump就是最直接的工具,不要全量抓,那样磁盘瞬间就满了,只抓取带有SYN标志的包或者限定端口和包长度。

tcpdump -i eth0 -nn -c 50000 -w /tmp/ddos_capture.pcap 'tcp[tcpflags] & (tcp-syn) != 0'

这条命令抓取前五万个SYN包存到指定文件,不会把磁盘撑爆,又能保留最关键的握手阶段数据。同时把系统日志、应用日志、连接状态快照全部外传。netstat命令能快速看到当前所有连接状态,统计每个IP的连接数,攻击源IP往往在这个阶段会暴露出来。

netstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -nr | head -20

这些数据不要只存在被攻击的机器上,攻击可能导致机器宕机或者被机房拔线,要通过脚本定时上传到外部存储或者另一台服务器。拿到这些原始数据,溯源才有了基础。

从流量特征判断攻击类型

DDoS攻击早就不是简单的流量轰炸了,攻击者会混合多种手法。通过分析抓包文件,能看出攻击属于哪一类。如果抓包里面大量短小的SYN包,源IP随机伪造,那就是SYN Flood,打的是TCP握手阶段的资源消耗。如果大量UDP包涌向随机端口,包长度固定,多半是UDP Flood或者利用DNS、NTP等协议做反射放大攻击。还有一类特别难缠的是CC攻击,它不靠带宽取胜,而是模拟正常用户行为,大量请求那些需要数据库查询的动态页面,比如搜索接口、登录接口,几百个请求就能把数据库连接池打满。

判断清楚攻击类型,才能针对性做策略。SYN Flood和UDP Flood这类四层攻击,靠机房清洗设备和内核参数调优就能解决大半。CC攻击这类七层攻击,必须在应用层做行为分析和人机验证。很多人把WAF规则配得特别严格,结果误杀大量正常用户,这就是没分清楚攻击类型导致的。

源IP溯源要分层次进行

DDoS攻击的源IP绝大多数是伪造的,或者是被控制的肉鸡IP,直接封IP意义不大。但溯源不是要找到攻击者真人,而是要搞清楚攻击资源的来源和规模。第一层,从抓包数据里提取所有源IP,做地理位置分布和AS号归属分析。如果大量IP集中在某个地区的某个运营商网段,可以联系上游对该网段做临时限速。第二层,分析攻击流量的TTL值和IP头的Identification字段,同一批肉鸡往往有相似的协议栈指纹,这能帮你判断攻击工具的类型。第三层,如果攻击是针对业务接口的CC攻击,要分析请求头里的User-Agent、Referer、Cookie等字段,攻击脚本往往在这些地方露出马脚,比如User-Agent全是同一个老旧版本,或者Referer为空但直接请求需要登录的接口。

真正有经验的团队,会在攻击流量里找那些“不变的东西”。反射放大攻击的反射服务器列表、肉鸡的常用端口特征、攻击脚本的HTTP请求头顺序,这些特征一旦提取出来,就能在后续防御中提前拦截。溯源不是为了追到某个人,而是为了建立攻击者画像,知道他手里掌握着什么资源,下次他再来,你的防御系统能一眼认出来。

服务器层修复不是改几个参数就完事

攻击过后服务器往往处于半死不活的状态,连接数残留、内存被占满、文件句柄耗尽。首先要清理残留状态,把TIME_WAIT过多的连接回收掉,调整内核参数让系统更快释放资源。

sysctl -w net.ipv4.tcp_tw_reuse=1
sysctl -w net.ipv4.tcp_tw_recycle=1
sysctl -w net.ipv4.tcp_fin_timeout=30

但光改这些不够,syn backlog队列和半连接队列的大小要重新评估。如果服务器内存足够,把tcp_max_syn_backlog调到8192甚至更高,同时开启SYN Cookie机制,让系统在syn队列满的时候也能继续处理新连接。

sysctl -w net.ipv4.tcp_max_syn_backlog=8192
sysctl -w net.ipv4.tcp_syncookies=1

应用层面,Nginx或Apache的连接数限制必须做。Nginx用limit_conn和limit_req模块限制单IP的并发连接数和请求速率,但要注意白名单自己的监控系统和CDN回源IP。很多人配了限速结果把搜索引擎蜘蛛也拦了,SEO流量直接掉没。限速策略要分路径,静态资源可以宽松,登录和搜索接口必须严格。

limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
location /login {
    limit_req zone=login burst=3 nodelay;
}

数据库层面,攻击期间可能产生了大量慢查询,连接池被打满后很多正常请求超时。恢复后要检查慢查询日志,把那些被攻击触发的高消耗SQL做优化,该加索引加索引,该做缓存做缓存。Redis或Memcached这类缓存层在攻击后可能被写入了大量垃圾数据,需要做一次清理或者设置合理的过期策略。

架构层要彻底改造才能抗住下一次

单机防御永远有上限,攻击者只要堆资源就能突破。正确的思路是把攻击面分散,让攻击者找不到一个能打穿的集中点。域名解析层面要做多线负载均衡,不要把全部流量指向同一个IP。如果业务允许,静态资源和动态接口分离部署,静态资源走CDN,动态接口走高防线路,这样即使动态接口被打,静态页面还能正常访问,用户至少能看到网站还在,不会以为你彻底跑路了。

CDN和反向代理层要配置好回源保护,真实服务器IP绝对不能暴露。历史上大量攻击案例都是因为某个子域名或者邮件头泄露了源站IP,攻击者直接绕过CDN打源站。检查方法很简单,用全球不同节点去ping你的域名,看返回的IP是不是都是CDN节点。同时查DNS历史记录,看看以前解析过的IP有没有裸奔在公网上。

业务接口要做分级处理。搜索、登录、注册、支付回调这些高消耗接口,单独部署到性能更强的机器上,并且在前置网关层做严格的频率控制。一个用户一秒钟不可能搜索十次,也不可能连续输错密码二十次,这些异常行为在网关层直接拒绝,根本不往后端透传。网关层可以用OpenResty或者APISIX这类高性能方案,用Lua脚本写限流逻辑,灵活度和性能都比传统Nginx配置高得多。

日志和监控体系要重建

攻击过后最大的教训往往是监控不够细。带宽打满这种级别的告警谁都能收到,但CC攻击刚开始的时候,可能只是响应时间变长了一点点,数据库连接数缓慢上升,这种渐变式的异常如果不做基线监控,根本发现不了。需要把每个核心接口的响应时间、错误率、QPS做成时序数据,设定动态阈值,一旦偏离正常波动范围就触发告警。

日志方面,访问日志要实时输出到消息队列,再灌入分析引擎。攻击期间产生的日志量可能是平时的几百倍,磁盘写满会导致服务不可用,所以日志必须异步外送,不在本地堆积。分析引擎可以用开源的ClickHouse或者Elasticsearch,对攻击期间的日志做聚合分析,找出被集中攻击的URL路径和参数特征,这些数据直接喂给WAF做规则更新。

还有一个容易被忽略的点,DNS查询日志。很多DDoS攻击会伴随DNS查询洪水,或者攻击者在对你的域名做子域名爆破。监控DNS查询的异常波动,能在攻击发起前几分钟就察觉到征兆。

建立应急响应流程比技术更重要

技术方案写得再好,真到出事的时候如果没人知道该按什么步骤操作,照样手忙脚乱。应急响应流程要精确到人,谁负责切流量,谁负责联系机房,谁负责通知客户,都要有明确的A/B角。流程要定期演练,不能只停留在文档里。演练的时候会发现很多问题,比如高防IP的切换脚本因为API版本更新已经跑不通了,或者负责人的手机号换了没更新通讯录。

攻击结束后的复盘报告必须写,记录攻击时间线、攻击类型、影响范围、恢复步骤、暴露的问题。这份报告不是为了应付领导,而是给团队积累经验。下次同样类型的攻击再来,看到监控曲线的形状就能判断出大概是什么攻击,直接走对应的处理流程,响应时间从半小时压缩到五分钟。

最后要强调的是,DDoS攻击防御没有一劳永逸的方案。攻击者的手法在进化,你的防御也要持续迭代。每次攻击都是一次免费的渗透测试,它暴露了你架构里最薄弱的那个点。把那个点补上,下次攻击者就得花更大的成本来找新弱点。当你的防御成本远低于攻击者的攻击成本时,你就赢了。