DDoS攻防实战:从攻击原理到纵深防守与应急处置
DDoS这三个字母,做过运维和后台开发的人看到都会心里一紧。我见过凌晨三点被告警电话叫醒、盯着流量图一路飙红的场景,也处理过因为一个没做限流的公开接口被刷到整台机器失联的事故。这篇内容就把DDoS攻击这件事从头到尾拆开讲清楚:它到底是什么、攻击者是怎么打的、我们作为防守方又该怎么一层层把损失控制住。不管你是刚入行的后端开发、负责线上稳定的运维,还是自己维护着一台小服务器的独立开发者,这里面的思路和实操都能直接拿去用。我会尽量少讲空话,多讲原理和能落地的配置,把那些文档里不写、只有真被打过才知道的细节都摊开来说。
1. 先搞清楚DDoS到底在打什么
1.1 从一次真实的告警说起
假设你维护着一台对外提供接口服务的机器,某天下午监控突然报警:入方向带宽从平时的20Mbps直接冲到接近带宽上限,CPU占用不算高,但所有正常用户的请求开始超时,页面转圈,接口返回502。你登录上去看,netstat里一堆来自不同IP的半连接,ss -s显示大量SYN_RECV状态的连接堆积。这时候基本可以判断,你正在被DDoS。
DDoS全称是分布式拒绝服务攻击,核心逻辑特别朴素:用远超目标处理能力的请求量,把目标的带宽、连接数、CPU或者应用层资源占满,让正常用户进不来。注意“分布式”三个字,攻击流量不是来自一台机器,而是来自成百上千台被控制的设备,这些设备可能是被感染的服务器、家用路由器、摄像头,也可能是被滥用的云主机。单台机器打不动你,但一千台一起打,效果就完全不一样了。
它和普通的高并发压力测试有个本质区别:压测是你自己可控的、有明确目标的流量,而DDoS是恶意的、来源分散的、往往还伪装成正常请求。防守方要在海量流量里区分“谁是坏人”,这才是最难的。
1.2 攻击者图什么
很多人以为DDoS就是黑客炫技,其实背后动机很现实。常见的有几类:一是敲诈勒索,打你几个小时然后发邮件要钱;二是商业竞争,把对手的促销活动打瘫;三是报复或者恶作剧,比如游戏服务器被打;四是声东击西,用DDoS把安全团队的注意力吸引到流量层面,真正的入侵发生在应用层。理解动机很重要,因为它决定了攻击的持续时间和强度——勒索型往往是短时高强度脉冲,竞争型可能持续数天。
1.3 防守方的三个基本盘
在动手配置之前,先建立一个整体认知。防守DDoS不是靠某一个设备或者某一行代码,而是三层配合:
- 带宽层:你有多少余量扛住流量洪峰,这决定了你能不能“硬抗”。
- 协议层:能不能在网络层就把异常连接识别并丢弃,减少对后端的冲击。
- 应用层:即使流量到了应用,能不能靠限流、鉴权、缓存把无效请求挡在业务逻辑之外。
这三层任何一层缺失,都会让另外两层压力倍增。后面所有内容都是围绕这三层展开的。
2. 攻击类型拆解:从流量洪峰到慢速折磨
2.1 流量型攻击:用带宽硬砸
流量型攻击是最直观的一类,目标就是把你的出口带宽塞满。典型代表是UDP Flood和ICMP Flood,攻击者控制大量设备向目标IP发送大量UDP包或者ICMP包,这些包本身不需要建立连接,发出去就完事,成本极低。还有一类是反射放大攻击,攻击者伪造源IP向一些公开的公共服务发请求,让这些服务把放大的响应打向受害者。比如某些基于UDP的查询协议,一个小请求能换来几十倍甚至上百倍的响应,攻击者用很小的成本就能制造巨大的流量。
这类攻击的特点是流量大、特征相对明显、但难以溯源。防守上主要靠带宽冗余和上游清洗。我个人的经验是,如果你的业务带宽是100Mbps,那至少要按能扛住数倍于它的突发流量来规划,否则一旦被打,连登录管理后台都费劲。
2.2 协议型攻击:专攻连接状态
协议型攻击不追求塞满带宽,而是耗尽服务器或中间设备的连接表。最经典的是SYN Flood:TCP三次握手的第一步是客户端发SYN,服务器回SYN-ACK并分配资源等待最后的ACK。攻击者发大量伪造源IP的SYN包,服务器回了SYN-ACK却永远等不到ACK,这些半连接会占满连接队列,正常用户的新连接就进不来了。
这类攻击的流量可能并不大,但对服务器的杀伤力很强,因为它打的是协议栈的资源上限。防守的关键在于缩短半连接超时、启用SYN Cookie、以及在边界设备上做连接数限制。下面这段是Linux下调整SYN相关参数的一个参考,实际值要结合你的业务量来定:
# 启用SYN Cookie,半连接队列满时用加密方式回应,避免资源被占死 net.ipv4.tcp_syncookies = 1 # 半连接队列长度,适当调大 net.ipv4.tcp_max_syn_backlog = 8192 # 减少SYN-ACK重试次数,加快半连接释放 net.ipv4.tcp_synack_retries = 2 # 缩短TIME_WAIT回收时间 net.ipv4.tcp_fin_timeout = 15注意:这些参数不是越大越好。
tcp_max_syn_backlog调得过大,在真实攻击下反而可能让内存压力上升,建议先在测试环境验证,再逐步调整。
2.3 应用层攻击:伪装成正常用户的慢性毒药
应用层攻击(也叫CC攻击)是我认为最难缠的一类。它不追求大流量,而是模拟正常用户行为,反复请求那些消耗资源最多的接口,比如登录、搜索、下单、导出报表。攻击者可能用代理池、慢速请求、随机参数等方式绕过简单的频率限制,让服务器CPU和数据库连接被慢慢耗干。
这类攻击的可怕之处在于:流量图上看起来风平浪静,但业务已经不可用了。防守它靠的不是带宽,而是精细化的限流、行为分析和人机识别。比如对登录接口做基于IP和账号的双维度限流,对搜索接口加缓存,对可疑请求做挑战验证。这部分后面会专门展开。
2.4 一张表看清三类攻击的差异
| 攻击类型 | 主要目标 | 流量特征 | 典型手法 | 防守重点 |
|---|---|---|---|---|
| 流量型 | 出口带宽 | 流量巨大且突发 | UDP Flood、反射放大 | 带宽冗余、上游清洗 |
| 协议型 | 连接表资源 | 流量中等、连接异常 | SYN Flood、连接耗尽 | 协议栈调优、连接限制 |
| 应用层 | CPU与业务资源 | 流量接近正常 | CC攻击、慢速请求 | 限流、缓存、人机识别 |
把这张表记在心里,遇到告警时先判断是哪一类,再对症下药,比盲目加机器有效得多。
3. 防守体系怎么搭:从边界到应用的纵深布局
3.1 第一道防线:把流量挡在门外
对于有一定规模的业务,最省心的做法是借助上游的清洗能力。很多云服务商和CDN都提供DDoS防护,原理是在流量到达你的源站之前,先经过它们的清洗中心,把异常流量过滤掉,只把正常流量回源。这样做的好处是你不用自己买昂贵的抗D设备,坏处是清洗有阈值,超过套餐额度还是会被打穿,而且回源流量如果没做好隐藏,源站IP一旦暴露,攻击者可以绕过清洗直接打源站。
所以这里有个关键动作:隐藏源站IP。具体做法包括用CDN回源、源站只允许清洗节点的IP访问、不在任何公开渠道泄露真实IP。我见过不少案例,防护买了一大堆,结果源站IP在某个历史DNS记录里躺着,攻击者直接绕过防护打源站,前面的投入全白费。
3.2 第二道防线:边界设备的连接管控
如果流量已经到了你的边界设备(比如负载均衡或者防火墙),就要在这里做连接层面的管控。核心思路是限制单个来源的连接数和请求速率。以常见的反向代理为例,可以配置单IP的并发连接上限和请求速率上限:
# 定义限流区域,按IP维度,每秒10个请求 limit_req_zone $binary_remote_addr zone=perip:10m rate=10r/s; # 定义连接数限制区域 limit_conn_zone $binary_remote_addr zone=conn_perip:10m; server { location /api/ { # 突发允许20个,超出排队 limit_req zone=perip burst=20 nodelay; # 单IP最多50个并发连接 limit_conn conn_perip 50; proxy_pass http://backend; } }这段配置的意图很明确:正常用户每秒几个请求完全够用,而攻击脚本往往在短时间内发起大量请求,会被直接拒绝。burst参数给了一定的突发余量,避免误伤正常用户的页面加载(一个页面可能同时发起多个静态资源请求)。nodelay表示超出的请求不排队直接拒绝,这在被攻击时能更快释放资源。
提示:限流阈值一定要结合真实业务数据来定。上线前先统计正常流量的P99请求速率,阈值设在它的2到3倍比较稳妥,拍脑袋定一个数字很容易误伤。
3.3 第三道防线:应用层的精细化识别
到了应用层,防守手段就更丰富了。第一是缓存,把那些读多写少、结果相对固定的接口结果缓存起来,攻击者反复请求同一个资源时直接命中缓存,不打到数据库。第二是人机识别,对可疑请求弹出验证挑战,正常用户点一下就能过,脚本则过不去。第三是行为分析,比如同一个IP在短时间内用不同账号登录、请求参数呈现明显的机器特征,这些都可以作为拦截依据。
这里要强调一个原则:应用层防守要尽量前置。能在网关层拦掉的请求,就不要让它进到业务代码里。因为一旦进了业务逻辑,哪怕最后返回了错误,CPU和数据库连接也已经被消耗了。我一般会在网关层做统一的限流和黑名单,业务代码里只保留必要的业务校验。
3.4 监控与告警:被打之前先知道
防守体系里最容易被忽视的是监控。很多团队是被打瘫了才发现,这时候已经晚了。基础的监控至少要覆盖:入方向带宽、连接数、新建连接速率、各状态连接占比、应用层QPS和错误率。把这些指标配上阈值告警,一旦出现异常突增,能第一时间响应。
我习惯在流量图上标注历史峰值,这样一眼就能看出当前流量是不是异常。另外,新建连接速率这个指标特别有用,SYN Flood这类攻击往往表现为新建连接数暴涨而带宽变化不大,光看带宽是发现不了的。
4. 实操:从告警到止血的完整处置流程
4.1 第一步:快速判断攻击类型
收到告警后,别急着上手段,先花一两分钟判断类型。看三个东西:带宽曲线、连接状态分布、应用层QPS。带宽打满但连接数正常,大概率是流量型;带宽正常但SYN_RECV堆积,是协议型;带宽和连接都正常但应用QPS异常、响应变慢,是应用层。判断清楚类型,后面的处置才有方向。
4.2 第二步:临时止血的几板斧
针对不同情况,临时手段也不一样。流量型攻击,最直接的是联系上游做流量清洗或者临时切换高防线路;协议型攻击,先在系统层调优协议栈参数、开启SYN Cookie,同时在边界设备上限制单IP新建连接速率;应用层攻击,立刻收紧限流阈值、开启人机验证、把明显恶意的IP段加入黑名单。
这里分享一个实操细节:黑名单要动态维护,不要一次性写死。攻击IP是不断变化的,写死的黑名单很快失效。更好的做法是基于请求特征做拦截,比如某个User-Agent、某个异常的参数模式,这类特征比IP稳定得多。
4.3 第三步:验证止血效果
上完手段后,要立刻验证效果。看带宽是否回落、连接数是否恢复正常、正常用户的请求成功率是否回升。这里有个坑:有时候攻击流量降下来了,但业务还没恢复,因为之前的连接堆积或者缓存击穿还在影响。所以要同时看系统层和业务层的指标,不能只看一个。
4.4 第四步:事后复盘与加固
攻击过去之后,复盘比处置本身更重要。要搞清楚:攻击从哪个入口进来的、哪些防护生效了、哪些没生效、暴露了什么问题。常见的加固方向包括:补充限流规则、隐藏源站、优化缓存策略、完善监控告警。我一般会把这些整理成一个清单,逐项跟进,避免下次再踩同样的坑。
5. 常见问题与避坑经验
5.1 为什么加了防护还是被打穿
最常见的原因是源站IP暴露。防护设备只保护了经过它的流量,攻击者如果知道你的真实IP,直接打源站,防护就形同虚设。排查方法很简单:从外部网络直接访问源站IP,如果能通,说明源站没做好隔离。加固方式是源站只允许防护节点的IP访问,其他一律拒绝。
第二个原因是防护阈值设置不合理。有些防护套餐的清洗阈值设得很高,小流量攻击根本不触发清洗,等触发的时候业务已经受影响了。这个要和防护服务商确认清楚,必要时调低触发阈值。
5.2 限流会不会误伤正常用户
会,所以阈值要基于数据来定。我的做法是先只记录不拦截,观察一段时间正常流量的分布,找到P99值,再把阈值设在它的合理倍数上。另外,限流要分接口设置,登录接口和静态资源接口的合理速率完全不同,一刀切必然误伤。
5.3 小团队没有预算买高防怎么办
这是很多独立开发者的现实问题。可行的思路是:第一,尽量用CDN隐藏源站,很多CDN自带基础的DDoS防护;第二,在应用层把限流和缓存做扎实,这能挡掉大部分应用层攻击;第三,做好监控和应急预案,真被打的时候能快速切换或者临时扩容。预算有限的情况下,把钱花在监控和架构优化上,性价比往往比买昂贵的防护设备高。
5.4 一张速查表
| 现象 | 可能原因 | 快速排查 | 处置方向 |
|---|---|---|---|
| 带宽打满、连接正常 | 流量型攻击 | 看入方向带宽曲线 | 上游清洗、切换线路 |
| SYN_RECV大量堆积 | SYN Flood | ss -s看连接状态 | 开SYN Cookie、限新建连接 |
| QPS异常、响应变慢 | 应用层攻击 | 看接口维度的QPS和耗时 | 收紧限流、加缓存、人机验证 |
| 防护生效但业务仍慢 | 源站暴露或缓存击穿 | 直连源站测试 | 隔离源站、优化缓存 |
5.5 几个容易被忽略的细节
第一,日志本身也可能成为攻击面。被攻击时如果每个请求都写详细日志,磁盘IO可能先扛不住。建议对高频异常请求做采样记录,而不是全量落盘。第二,健康检查要单独放行,否则限流规则可能把负载均衡的健康检查也拦了,导致后端被误判为不健康而摘除。第三,定期演练,防护配置写完不代表有效,找个低峰期做一次小规模压测,验证限流和告警是否按预期工作,这比出事时手忙脚乱强得多。
我在实际维护中发现,DDoS防守这件事,技术手段只占一半,另一半是预案和响应速度。配置再完善,如果没人盯着告警、没人知道第一步该做什么,效果也会大打折扣。所以除了把上面的技术点落地,建议每个团队都准备一份简单的应急处置清单,写清楚谁负责判断、谁负责联系上游、谁负责业务侧降级,真出事的时候照着做就行。这套东西平时看着不起眼,关键时刻能省下大量时间。