DDoS攻击原理与防御实战:从SYN Flood到CC攻击的完整防护指南
干过几年一线运维和应急响应的人几乎都躲不开DDoS这个话题。你正在维护的系统本来一切正常某天下午流量监控曲线突然直冲天花板紧接着用户反馈页面打不开、报警电话响个不停——这种场景我经历过太多次。今天把关于DoS与DDoS攻击原理这块老底翻出来把TCP、UDP、CC攻击这几个最常见的打法讲透再聊聊从接入层到应用层到底怎么防用什么参数、踩过哪些坑。这篇文章适合刚入门的运维、安全岗位新人也适合正在被攻击困扰、急于找排查思路的同学。只要你手上有对外服务的系统这些内容迟早用得上。1. 先搞明白DoS、DDoS到底攻击的是什么1.1 从一次群体拥堵说起把互联网比作一条双向车道。一个普通用户访问你的网站相当于一辆车在车道上正常行驶。DoSDenial of Service拒绝服务攻击就是恶意方向的某个人故意在繁忙路口长时间停车、反复倒车让原本的道路陷入拥堵真正想通过的正常车辆全部被堵死。服务器收不到合法连接即使收到了也没有资源处理于是表现为无法访问、请求超时、服务不可用。DDoSDistributed Denial of Service分布式拒绝服务则更进一步——攻击者操控成千上万的肉鸡被病毒感染、被远程控制的主机同时发动请求相当于几百条车道、几百个路口同时被恶意车辆占满。单个肉鸡的流量本来微不足道合在一起就成了几十G甚至几百G的洪流把机房出口带宽硬生生打满。这也是分布式攻击最让人头疼的一点它不是一台设备在搞你而是一个规模可观的车队在拆路。1.2 攻击核心带宽耗尽与资源耗尽攻击最终目标只有两个耗尽网络带宽或者耗尽服务器资源。带宽耗尽很好理解机房出口带宽是有限的比如你的服务托管在一个10Gbps的机柜里攻击流量只要超过这个数值合法流量就被挤出局了谁都进不来。UDP Flood、DNS反射放大这类攻击主要目的就是打带宽。资源耗尽则更精细一些攻击者可能不追求把带宽打满而是用大量半成品连接、慢速连接、高消耗的请求把服务器的CPU、内存、连接表、进程槽位占满。服务器明明还在运行但它的队列已经满了新来的合法请求根本没有机会被处理。SYN Flood和CC攻击都属于这一类。理解这两个目标是后面设计防御方案的基础——你得先知道你正在防御的是什么东西。1.3 攻击为什么难防两个天然劣势防御方在DDoS面前有天然的劣势如果你不了解这些劣势防御方案很容易想得太天真。第一攻击流量与正常流量混在一起尤其是在应用层CC攻击场景下攻击者的请求可能是拿真实浏览器内核发包的普通特征几乎一模一样区分合法与非法请求本身就难。第二攻击源分布极广且高度分散你封了一个IP后面还有成千上万个IP。传统的封禁策略放到DDoS面前就像用筛子拦洪水效果十分有限。所以成熟的防御体系一定不是一招制敌而必须是多层配合、层层过滤。2. 常见攻击手法拆解TCP、UDP、CC到底怎么打的2.1 TCP SYN Flood冲着半连接队列去的TCP握手过程大家都清楚客户端发一个SYN报文服务器回一个SYNACK客户端再回一个ACK然后才开始传数据。SYN Flood攻击正是掐在这个流程的第二步。攻击者伪造大量源IP发送海量SYN报文但从不完成最后一步握手。操作系统接收到SYN后会为这个半连接分配资源放进一个叫做SYN队列半连接队列的地方等待ACK确认。每次请求分配一小块内存量大了就会出现问题队列有上限排满了之后正常的SYN请求就被丢弃合法用户连第一次握手都完成不了。表现出来就是连接超时卡在加载中。我见过有同学问设置大一点的队列不就行了问题是攻击者可以持续高速发送你把队列从1024调大到4096、8192他们只需要把发送速率同步提高几倍。配额永远会被冲垮而且调大的队列本身也占内存。真正有效的做法是对SYN报文做速率限制、启用SYN Cookie机制把半连接状态的资源开销转移给验证环节。这部分的细节我在第四节讲配置。2.2 TCP全连接攻击耗内存、耗CPU比SYN Flood更狡猾的是TCP全连接攻击。攻击者用真实IP完整走完三次握手建立大量合法连接然后不发数据或者发送极慢的数据就静静地占着连接。服务器端每个连接都需要维护socket、队列、文件描述符连接数到了上限新连接就进不来了。这类攻击在防护上更麻烦因为从TCP状态看它们就是一堆活着的连接你很难界定用户到底是网络慢还是故意不动。常见的应对思路是在应用层加超时回收机制空闲连接超过N秒断开、通过负载均衡限制单IP连接数和新建连接速率。曾遇到一个案例某后台管理系统的默认keep-alive超时配置成了数百秒结果攻击者只需要建立有限数量的连接就能把业务进程的连接数顶到接近上限排查了半天才找到是这类问题——这种细节型攻击反而比无脑大流量打起来更精准。2.3 UDP Flood无连接、无监管的流量洪水UDP和TCP最大的区别在于TCP有三次握手、有状态管理UDP则完全没有连接概念发送方只需要把数据包丢出去接收方就得处理。UDP协议结构的轻量让它很适合做实时音视频、DNS查询这些场景但同样让它成了流量型攻击的完美载体。UDP Flood攻击就是发大量UDP包砸向目标主机。接收方收到这些包后操作系统必须逐个判断这个端口有没有对应的应用在监听没有就把包丢弃。但这个判断丢弃过程本身需要CPU消耗大量垃圾UDP包同时到达时CPU就被拖垮了。更凶险的是UDP反射放大攻击攻击者向互联网上的公共DNS、NTP服务器发送一个源地址被伪造成受害者的请求这些服务器会把放大几十倍甚至上百倍的响应数据回给受害者。攻击者可能本身只需要几十Mbps的流量经过放大之后落到你头上就成了几个Gbps甚至更高直接把带宽出口打满。这也是运营机房的人特别头疼的一种攻击方式。2.4 CC攻击比流量洪水更细腻的杀伤接下来是我们做应用防护时碰得最多的一种CC攻击Challenge Collapsar挑战黑洞。它针对的是应用层协议。攻击者模拟真实浏览器行为大量请求网站的动态接口——比如登录接口、查询接口、支付下单接口。每个请求看起来都特别正常带上了User-Agent、Cookie甚至自动执行了JS逻辑。但请求频率极高、并发极大把后端的数据库、进程池、CPU全部拖垮。为什么说CC攻击难防因为它不依赖带宽单台高配服务器加几个肉鸡就能打出一个不太大但足够致命的请求量。流量层面上你可能只看到比平时高几倍不仔细看根本发现不了异常。它消耗的是应用处理和数据库查询的资源这种消耗是业务逻辑本身带来的你没法简单地用非Get不处理之类的规则一刀切。电商大促遇到CC攻击最典型的现象就是带宽没满、网络正常但是接口响应时间从几十毫秒飙升到几秒数据库连接数被占满错误日志里全是超时。防御CC攻击的核心就在于识别出谁在发疯然后精准地把它的请求挡掉要么用频率限制要么用行为分析要么上验证码必要的时候请云清洗团队介入。这部分我在第五节详细展开。3. 防御体系怎么搭从接入层到应用层一层一层减3.1 先想清楚你要防的是流量还是业务很多人一上来就找高防IP、买超大带宽结果发现买了之后攻击依然打穿为什么因为没搞清楚防御目标。如果是纯UDP大流量攻击你需要的是带宽冗余和流量清洗能力如果是SYN Flood你需要的是内核参数调优和防火墙策略如果是CC你需要的是WAF规则、频控和验证码。攻击路径不一样的防御武器完全不一样。我见过一个典型反面教材某团队给电商网站配了10Gbps高防带宽结果被CC攻击打挂了。因为CC根本不打带宽打的是数据库连接。高防IP只管帮你挡网络层和传输层的大流量洪水应用层的流量不大但请求很密它不一定管得了。所以搭防御体系之前先做一个自检你的核心服务是什么业务高峰期的正常流量是多少如果攻击来了最可能被打的层是哪里这些问题清楚了方案才不跑偏。3.2 分层防御的整体思路洋葱模型成熟的架构讲求层防御defense in depth每一层都尽量拦截一部分恶意流量最后一层兜底。落到DDoS场景我习惯按四层来设计。接入层/网络层机房边界、云厂商高防IP。这里负责过滤超大流量洪水通过路由黑洞、流量清洗、DDoS包过滤等手段把明显恶意的大流量挡在进入业务系统之前。核心指标是清洗能力和冗余带宽。传输层防火墙与负载均衡器。这里负责管理TCP连接状态可以启用SYN Cookie、限制并发连接数、设置单IP速率上限。它防的是SYN Flood这类针对连接状态的攻击。应用层WAFWeb应用防火墙、反向代理NGINX等。这里处理HTTP协议层面的攻击比如限流、封禁异常UA、IP黑白名单、验证码等。它防的是CC攻击和慢速攻击。数据层数据库连接池、缓存层。通过连接数限制、缓存热点数据、限流中间件来保护最底层的数据库不被拖垮。很多人容易忽略这一层但一旦攻击穿透了上层最终死掉的往往是数据库。3.3 再说说为什么要分层而不是只依赖一层只依赖一层防御最大的问题就是单点失效。你如果只买了云清洗攻击者换一种不占带宽的CC攻击方式清洗设备可能形同虚设。你如果只做了NGINX限流攻击者绕开应用直接打源站IP你的限流规则根本碰不到它。分层防御的思路本质上是让每一层都成为攻击者绕不过去的关卡即便某一层被打穿了下一层还能把损害限制在可控范围内。实战里即便主防御承担了99%的流量清理剩下那1%漏到业务层如果业务层毫无防御能力照样能让服务崩溃。所以别嫌麻烦每一层的扣子都得扣上。4. 实操环节模拟某系统防御配置实录下面我以模拟项目X一个公网电商API系统为例给出一套可以直接参考落地的防御配置。这套配置是我在多个项目里实测过、效果比较稳的方案。整体思路是先调操作系统内核参数再配防火墙和反向代理最后在应用侧加保护。每一层都要动手验证效果不能配完就丢。4.1 操作系统内核参数调优第一道闸门针对SYN Flood和各类TCP连接攻击Linux内核的配置非常关键。在/etc/sysctl.conf里加上以下内容# 开启SYN Cookie防止SYN Flood net.ipv4.tcp_syncookies 1 # 设置SYN半连接队列长度队列过小会导致正常握手被丢弃 net.ipv4.tcp_max_syn_backlog 8192 # 设置三次握手的SYN重试次数适当调低加快无效半连接回收 net.ipv4.tcp_syn_retries 2 # 设置SYNACK重试次数同样调低 net.ipv4.tcp_synack_retries 2 # 系统可使用的临时端口范围防止端口耗尽 net.ipv4.ip_local_port_range 1024 65535 # TIME_WAIT连接快速回收 net.ipv4.tcp_tw_reuse 1 # 设置最大跟踪连接数针对使用conntrack的场景 net.netfilter.nf_conntrack_max 1048576生效命令sysctl -p注意几个点。第一tcp_max_syn_backlog不能设得过分夸张因为每个半连接都会占用内存设成十几万可能导致内存不够。我一般从8192开始观察半连接队列占用的平均值再决定调大还是调小。第二tcp_tw_reuse只对客户端一侧有意义作用是快速复用TIME_WAIT状态的连接你如果拿它防服务端 TIME_WAIT 堆积效果有限那个场景应当调tcp_max_tw_buckets。第三所有参数改完后一定要观察业务是否正常比如某些老旧设备兼容性差SYN Cookie在某些极端NAT场景下可能导致握手失败这类生产事故并不少见。4.2 防火墙与负载均衡层动手限速接下来是防火墙与负载均衡。模拟项目X的系统部署在机房内核心入口是一台负载均衡设备后面挂了三台应用服务器。在负载均衡设备上用类似iptables的规则做防护很多商用负载均衡设备规则名字略有差异但思路一致# 限制单个IP每秒新建TCP连接数不超过100 iptables -A INPUT -p tcp --syn -m recent --name synflood --update --seconds 1 --hitcount 100 -j DROP # 限制单个IP并发连接数不超过200 iptables -A INPUT -p tcp --dport 80 -m connlimit --connlimit-above 200 -j REJECT # 针对UDP Flood限制单IP的UDP包速率 iptables -A INPUT -p udp -m limit --limit 200/s --limit-burst 400 -j ACCEPT iptables -A INPUT -p udp -j DROP这里有个细节值得特别说明ACCEPT规则执行顺序很重要。limit模块表示的是按速率放行超出速率的部分会落到后面的DROP规则所以一定要把-m limit的ACCEPT规则放在所有UDP规则的最前面否则后面的drop永远生效正常UDP业务也会被打死。在正式环境里改这些规则前我会先在一个测试端口上模拟观察效果确认不会影响正常外部DNS查询和NTP同步再全量应用。还有一个我踩过的坑一些业务用了第三方UDP日志上报通道部署规则前没注意一上线就把日志通道全掐断了排查了一天。4.3 NGINX反向代理层挡住CC攻击的第一道廓清CC攻击防御很大一部分要在NGINX这一层做。先看核心限流配置# 定义限速每个IP每秒最多5个请求允许瞬时10个请求突发 limit_req_zone $binary_remote_addr zoneperip:20m rate5r/s; # 定义连接数限制每个IP最多并发50个连接 limit_conn_zone $binary_remote_addr zoneperconn:20m; server { listen 80; server_name api.example.com; location /api/query { limit_req zoneperip burst10 nodelay; limit_conn perconn 50; proxy_pass http://backend; } }这里面最值得琢磨的是burst10 nodelay。burst表示突发许可允许在短时间内超出rate限制的请求排队处理nodelay表示超出burst的请求直接拒绝。如果你的业务有秒杀、大促这种瞬时高并发场景burst参数要适当调高否则会把正常用户误伤但调太高又会让限流失去意义。我的经验是先在压测环境用工具模拟正常业务峰值统计单IP的请求速率分布再把rate设定在正常用户永远不会触发非正常用户必然触发的区间。比如正常业务单IP峰值在1-2req/s那rate设5req/s就是合理的。前面说的全连接攻击在NGINX层还可以通过keepalive_timeout和client_body_timeout控制连接生命周期。有慢速攻击或者占连接不发的攻击时把这两个超时时间从默认的75秒降到10-15秒连接的清理速度会快很多。要注意的是如果业务方有长连接推送需求这些参数不能一刀切需要单独为长连接路径单独配置。4.4 应用与数据库层保护最脆弱的环节即使前面几层过滤了不少CC攻击仍可能有少量请求落到应用和数据库层。比如攻击者拿大量真实IP分布分散、单个IP速率不高但总请求量仍然能把数据库打死。这种情况需要在应用层加更懂业务的保护。第一招是加缓存。对于高频查询、但低敏感度的数据接口比如商品列表、新闻详情、字典配置尽量用Redis或本地缓存顶住不要让每个请求都打数据库。我遇到过一个真实系统某个接口被CC攻击后数据库有数千条慢查询查了一下发现大部分查询完全可以用缓存规避代码改成缓存优先、DB兜底之后抗住攻击的能力直接上升一个量级。第二招是加验证码。登录、注册、下单这类高频高价值接口当NGINX层检测到单位时间请求量异常时可以自动跳转验证码页面。虽然这会损失一点体验但总比服务完全瘫痪好得多。第三招是数据库连接池限制。在中间件或连接池配置中明确最大连接数比如最大200个连接超出后新请求排队而不是无限制新建连接。数据库本身也要打开慢查询日志和锁等待监控方便攻击发生时快速定位。5. 攻击发生时怎么办应急响应与排查技巧5.1 快速识别到底是攻击还是业务突增攻击发生后最容易犯的错误就是慌不择路。我建议你先冷静做几个判断确认到底是攻击还是业务突增。第一看时间规律。有没有规律是不是每天同一时段有没有可能是定时任务、数据同步触发了流量上涨第二看流量与连接数分布。打开监控面板如果带宽接近打满、SYN连接数暴涨、UDP流量暴增大概率是DDoS流量攻击如果带宽正常但TCP连接数增加、CPU使用率飙升、应用日志大量超时更可能是CC攻击。第三看请求来源。挑几个异常请求看UA、来源IP分布、请求参数是否集中且异常。攻击请求往往高度雷同比如UA集中在一个奇怪的值、IP集中在陌生的运营商网段、请求参数固定为某些词。在服务器上快速确认的命令# 查看当前TCP连接状态分布留意SYN_RECV和TIME_WAIT ss -ant | awk {print $1} | sort | uniq -c | sort -rn # 统计每个IP的连接数排序看看有没有单个或少数IP占比异常 ss -ant | awk {print $5} | cut -d: -f1 | sort | uniq -c | sort -rn | head -20 # 查看接口流量实时速率判断是否接近带宽上限 sar -n DEV 1 35.2 应急止损先扛住再排查如果确认是严重DDoS第一时间要做的是止损而不是花大量时间分析攻击源。核心思路是宁可误伤部分正常流量也要保障核心业务可用。第一步把域名解析临时切到高防IP或云清洗服务。前提是你提前买好了高防服务且知道控制台怎么操作。如果没买立即联系服务商开启黑洞或清洗这是最快见效的手段。第二步在上游防火墙上临时加更激进的规则比如降低单IP的SYN新建速率限制、扩大UDP DROP范围。第三步如果攻击持续且业务必须保持在线可以临时调整架构——把静态内容切到CDN源站做白名单访问只允许CDN回源IP通过这样攻击流量在CDN这一层就被消化大半了。我记得有个深夜处理故障的典型经历某线上签到服务在凌晨两点被DDoS打满带宽用户侧完全无法访问。我们当时的第一动作不是分析攻击类型而是联系IDC把流量切换到备用高防链路五分钟内恢复了基本访问。等核心业务恢复之后我们才慢慢去分析攻击源、特征和后续加固。这就是应急的优先级——先恢复、后分析顺序千万不要反。5.3 常见问题速查表我把这么多年踩过的坑整理成一个速查表供大家参考。问题现象可能原因处理方案带宽打满ping不通UDP Flood或反射放大攻击启用黑洞/高防清洗临时断开受影响端口带宽正常但网站卡顿CC攻击NGINX层限流、WAF规则、验证码、扩容带宽和连接池SYN_RECV连接数剧增SYN Flood开启SYN Cookie、调低syn重试次数、限制单IP新建速率连接数很多但CPU不高TCP全连接攻击/慢速攻击调低keepalive_timeout、加强空闲连接回收、限制单IP并发数据库连接被占满应用层攻击穿透后打DB数据库连接池限流、加缓存、限流中间件、重启后先恢复核心接口正常用户被误封限流阈值设置过严提高rate/burst参数、按用户维度如Cookie/Token限流5.4 被误封了怎么办不是所有请求都该挡防御系统上线后最容易出现的就是误杀。普通用户可能因为某个接口请求频率略高被NGINX层直接拒绝。处理这类问题有两个经验。第一限流维度尽量从IP维度提升到IPCookieToken维度单一IP可能多人共享比如公司出口NAT只按IP限流会误伤一整群人。第二加白名单机制。搜索爬虫、合作方回调、内部监控系统这些固定来源的请求应该直接放行不要参与限流。我实战里有个印象很深的案例某业务方接了一个外部数据服务商的回调接口回调频率本身远高于正常用户我们一开始没加白名单结果上线防护策略后回调全部被拦合作方数据出不来业务直接受影响。从那以后我每上线一套防护规则都会先圈出所有应当放行的来源再谈限制。6. 工具选型与方案取舍什么时候自建什么时候买高防6.1 自建防护的边界看到上文那些iptables和NGINX配置有人会想那我可以全自建了别急自建防护有非常明确的边界。自己搭的系统能防的方案主要集中在应用层、小规模流量和连接型攻击这些范围——比如SYN Cookie、NGINX限流、内核参数调优。这些方案依赖的是你单台机器的处理能力和带宽。如果攻击流量超过了机房给你分配的带宽比如200Gbps的UDP洪水打到你的10Gbps线路上那不管你的防火墙规则写得多么精妙物理链路先被堵死了自建方案瞬间失效。我见过不少团队被一次UDP放大攻击打掉几百G流量之后老老实实接入了高防服务。自建防御适合的是攻击规模在带宽可承载范围内、以应用层攻击为主、团队有足够运维能力和时间调优。6.2 高防IP和云清洗怎么选现在的云厂商和IDC一般都提供两类服务高防IP和流量清洗中心。高防IP更侧重于把源站IP隐藏起来让攻击流量直接打在高防节点上你只需要把业务域名解析过去。清洗中心则是在流量进入你机房之前先经过一套检测和过滤系统把异常流量识别并丢弃再把干净流量放进来。选型要点整理如下如果业务有高可用要求、需要对外提供服务且不能中断那高防IP是首选如果已有自建防火墙和流量分析能力且攻击量在可承受范围清洗中心就够用。高防IP的缺点是配置和收费都不低尤其遭遇大攻击时流量清洗费用可能飙升。我个人建议做一个双保险平时流量走自建线路遭遇大规模攻击时通过DNS切换到高防线路。这样既控制了日常成本又有了兜底能力。6.3 容量规划留多少带宽冗余才算够关于购买多少带宽冗余我直接给一套经验算法。先统计近30天业务流量峰值P95剔除过去被攻击的异常日在这个基础上留出2到3倍余量作为突发缓冲。如果业务属于大促型比如提前知道双十一或重大活动流量要涨5倍那带宽冗余按照活动峰值再乘1.5倍准备。这个数字不能拍脑袋。有一个我参与过的项目为了控制成本只保留了峰值流量的1.2倍冗余结果一次活动流量比预期高出50%差点被打挂。后来我们改成按P95×3规划虽然钱多花了一些但心里踏实多了。6.4 监控告警没有监控防御形同虚设最后一条也是很多人忽略的防御做得再完整没有监控告警等于白做。因为没有告警你根本不知道攻击已经在路上。我在生产环境里一般配置三层监控服务器层CPU、内存、带宽、TCP状态分布、业务层HTTP错误率、接口响应时间、订单成功率、安全层连接数Top IP、流量突变告警。每一层告警阈值都要单独调不能用默认值。比如TCP状态分布里SYN_RECV数量超过某个阈值就告警这个阈值一定是根据历史正常值算出来的而不是拍脑袋写一个超过100就报警——否则你可能会在深夜被无数条报警短信轰炸最后养成忽略告警的坏习惯。7. 从防护实例中学到的三件事这篇文章写到这里核心的攻击原理和防御手段都已经展开。最后我想分享三个从实际防护案例中沉淀下来的体会。第一个体会是不要试图防御所有类型的攻击先保住最核心的业务路径。很多团队的失败不是防御不到位而是想在攻击发生时什么都保。产品首页、登录、支付、订单查询、后台管理全都要保结果资源分散哪一个都没保住。我在模拟项目X里做过一次彻底的攻击演练最后的核心结论是先定义最小可用服务清单攻击压力过大时主动把非核心模块全部降级或关闭把带宽和计算资源集中分配给最核心的几个接口。演练之后我还在代码里加了紧急降级开关现在每次部署新版本那个开关的可用性都会被单独检查。第二个体会是防御是持续运营不是一次性配置。今天我写了内核参数、NGINX限流、高防线路但这些策略不是配置完就可以不管的。攻击手段在不断变化业务流量也在变化。我见过某团队把一套两年前调的限流阈值用到今天结果业务量上涨后大量正常用户被误杀。建议每季度做一次防护策略评审把规则、阈值和现状重新对齐这个工作不能省。第三个体会是攻击演练比等真被打了再学要值太多。我们团队基本每半年会做一次模拟DDoS攻击演练从流量监控、告警触发、应急响应、高防切换、攻击溯源到复盘报告全流程走一遍。第一次演练的时候一塌糊涂第二次还算顺畅到第三次基本能把应急时间压缩到十五分钟。真遇到攻击的那天大家虽然紧张但不慌乱按演练的流程一步步走最终顺利扛了过去。我强烈建议有业务在跑的同学一定要做一次这种实战演练——纸上谈兵一百次不如真刀真枪走一遍。最后说一句实在的关于攻击原理的学习目的是为了更好地防御、保护好自己和用户的系统。所有测试行为都应在你拥有合法授权的目标范围内进行。系统安全这条路上防住一次攻击的成就感远比发动一次攻击大得多。希望这篇东西能帮你少走一些我当年走过的弯路。