尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

TCP层三类DDoS攻击深度剖析:从SYN Flood到ACK Flood的防御实战

TCP层三类DDoS攻击深度剖析:从SYN Flood到ACK Flood的防御实战 干了这么多年网络安全应急十次被叫去处理DDoS八次都是TCP层的事。SYN Flood、SYN-ACK Flood、ACK Flood这几个名字听起来很接近但攻击路径和防护手段差别很大。这篇就把TCP协议下的三类常见DDoS攻击拆开讲清楚从原理到防护配置再到一次真实应急案例希望能帮到正在跟流量攻击死磕的同行。不管你是刚接手服务器的运维还是被老板叫去救火的开发顺着往下看至少能先分清攻击类型不会上来就乱封IP。1. TCP协议基础与三类攻击的关系1.1 三次握手的正常状态流转TCP是有状态的协议所有连接都必须从三次握手开始。客户端先发一个SYN包服务端收到后进入SYN_RCVD状态回复SYN-ACK客户端再回一个ACK服务端把这个连接从半连接队列移到全连接队列交给应用层accept连接才变成ESTABLISHED。整个过程可以用“敲门-应答-进门”来类比SYN是敲门SYN-ACK是里面问了一句“谁啊”ACK是门外的人应了一声门才会真正打开。很多人忽略的是服务端在收到SYN之后还没等到ACK之前就已经为这个连接分配了传输控制块TCB并把它挂到半连接队列里。这个队列有上限由内核参数控制。正常情况下半连接状态只会保留几秒第三次握手一到就转走了。但如果有人故意只敲门不应答服务端就只能一直等着队列里堆满没完成握手的连接后面的正常用户连敲门都敲不进去。这就是所有TCP层DDoS的根源协议本身信任通信双方而攻击者利用这种信任消耗资源。理解这一点再看下面三种攻击就顺了。1.2 SYN Flood的攻击原理SYN Flood是最经典的DDoS攻击方式。攻击者伪造大量源IP向目标服务器的业务端口发送SYN包目标服务器逐个回复SYN-ACK然后等待ACK。由于源IP是伪造的或者说根本不存在的这些SYN-ACK永远等不到回应连接就一直卡在SYN_RCVD状态。当伪造的SYN包足够多时半连接队列会被塞满。队满之后内核会直接丢弃新到的SYN或者启用SYN Cookie机制临时绕过队列但无论哪种方式正常用户的连接也无法按时建立。表现出来的就是网页转圈、接口超时、ssh能连上但业务端口进不去。有人会问为什么不直接用真实IP打如果攻击者用真实IP客户端因为收不到SYN-ACK很快会重试或放弃连接占用时间短。用伪造源IP服务端会一直等待把资源消耗拉满。即使半连接队列没满海量TCB占用的内存加上内核对这些包的查找处理也足以把CPU拖垮。1.3 从SYN Flood到SYN-ACK FloodSYN-ACK Flood和SYN Flood是亲兄弟但攻击路径不一样。常见的有两种第一种是反射型。攻击者把受害者的IP地址写进SYN包的源IP字段然后向大量开放端口的公网主机发SYN。这些无辜主机收到SYN后会正常回复SYN-ACK而回复的目标正是受害者。攻击者只需要用很小的流量就能骗来大量主机向受害者集中发送SYN-ACK包。受害者的服务器打开一看收到的全是“查无此连接”的SYN-ACK内核必须花费CPU去查TCP控制块查不到就回RST或直接丢弃。如果有状态防火墙/负载均衡挡在前面大量无关联的SYN-ACK还会迅速把会话表塞满导致所有新连接都无法建立。第二种是直接向目标服务器发送大量伪造的SYN-ACK包。这种包同样对应不到任何已建立的连接目的就是消耗服务器的处理能力。应急时先抓包看方向如果入口流量全是SYN-ACK且源IP五花八门基本可以判断是反射型如果只集中在少数IP可能就是直接打过来的。1.4 ACK Flood的攻击原理ACK Flood的打法更“欺负人”。TCP正常传输中ACK包几乎无处不在客户端收到数据要回ACK服务端收到数据也要回ACK。所以防火墙或者防护设备很难直接丢掉ACK包——否则正常业务全断了。攻击者发送大量ACK包但这些ACK包对应的连接要么根本不存在要么早就超时被回收了。服务端收到一个ACK会先查连接表发现没有这个连接然后回一个RST或者直接丢弃。一次查询本身开销不大但每秒几十万、上百万次查询CPU的软中断占用就会飙升锁竞争也会让整个协议栈变慢。如果攻击者再配合真实的三次握手先建立一堆合法连接再在长连接里发大量ACK包那更麻烦。从状态防火墙的角度看这些流量属于“已建立连接”全部在放行名单里普通限速规则根本拦不住。这种场景下问题已经不只是包量了而是连接数本身被打满。1.5 为什么攻击者偏爱TCP层UDP Flood也很常见但UDP是无状态的防御方可以直接在网关上针对源IP、目的端口做速率限制误伤面有限。TCP则不同它要求收包方必须维持连接状态攻击包和正常业务包长得几乎一样只用简单的ACL规则很难区分。打个比方UDP Flood像是有人往你门口倒垃圾你装个监控就能看到谁倒的TCP攻击像是成千上万个人轮流敲门每个人敲完就跑还把脸遮住你不能把门拆了也不能把所有敲门的人都当成坏人。攻击成本低、防御成本高、历史工具又多这就是它被用到泛滥的原因。2. 三类攻击的典型特征与危害判定2.1 攻击流量特征对比先看一张对比表方便在应急初期快速归类。攻击类型报文特征被攻击对象主要危害防御难点SYN Flood大量SYN源IP随机包长多在64字节左右服务器、防火墙、负载均衡半连接队列满新连接无法建立无法通过源IP简单封禁SYN-ACK Flood大量SYN-ACK部分来自无辜第三方受害服务器、状态防火墙连接表被塞满CPU查询开销大容易误伤正常握手响应ACK Flood大量ACK无对应连接或对应过期连接服务器、防火墙、负载均衡CPU软中断高conntrack表耗尽ACK在正常业务中占比高限速难定实际攻击里这三种经常混合出现。比如开始打SYN Flood看你把syncookies开了马上切一部分流量打ACK Flood让你两头顾不过来。判断时不能只看单一指标要综合PPS、连接状态、TCP标志位、源IP分布一起看。2.2 从服务器视角看攻击表现攻击到来时业务的第一反应一般是“慢”然后是“超时”最后是“连接被重置”。这些现象背后有几个比较典型的系统表现。第一个是半连接数量暴涨。执行ss -tan state syn-recv | wc -l正常可能不到几十攻击时能到几万甚至几十万。如果是SYN Flood这个数字几乎不会掉下来。第二个是内核日志提示。用dmesg或journalctl -k查看能看到类似possible SYN flooding on port 443. Sending cookies.的提示。这说明内核已经启动了SYN Cookie保护但同时也说明攻击量已经到达了半连接队列的上限。第三个是CPU软中断占用高。top里si数值很高或者mpstat -P ALL 1看到某个核的softirq接近100%多半是大量TCP包正在冲击协议栈。第四个是连接状态分布异常。比如ESTABLISHED连接数瞬间翻了几十倍但业务QPS没涨说明有大量“假连接”占着资源。再用netstat -s | grep -i syncookies看syncookies发送计数如果数字快速增长说明SYN Flood正在触发Cookie机制。2.3 判断攻击类型的方法我一般按下面这套流程判断基本不会偏先看整体包量用sar -n DEV 1或者ifstat看PPS。DDoS攻击包通常都是小包带宽可能只有几百兆但PPS能冲到几百万。再抓包采样用tcpdump -i eth0 -nn tcp port 443 -c 10000 -w attack.pcap存一下然后用Wireshark或者tshark看TCP标志位分布。统计纯SYN包数量命令里可以用tcp[tcpflags] tcp-syn ! 0 and tcp[tcpflags] tcp-ack 0这样的过滤条件。如果纯SYN占比超过90%基本就是SYN Flood。统计SYN-ACK或ACK占比。如果SYN-ACK特别多要再看源IP是不是分布在一堆固定IP上——固定IP可能是反射源随机IP可能是直接伪造。对比基线。把今天的连接数、PPS、状态连接数和过去一周同时间的数据放在一起看如果高出5倍以上且来源IP分散、TTL值高度集中大概率是攻击而不是业务突增。这几步做完攻击类型基本能锁定了。接下来才谈得上防护。3. 防护体系设计与核心参数调优3.1 Linux内核参数防SYN Flood单机防护的第一步是调内核这也是成本最低、见效最快的兜底措施。以下参数在攻击时可以直接临时生效确认没问题后写入/etc/sysctl.conf做持久化。sysctl -w net.ipv4.tcp_syncookies1 sysctl -w net.ipv4.tcp_syn_retries2 sysctl -w net.ipv4.tcp_synack_retries2 sysctl -w net.ipv4.tcp_max_syn_backlog4096 sysctl -w net.core.somaxconn4096 sysctl -w net.ipv4.tcp_abort_on_overflow1挨个说下我为什么这么调。tcp_syncookies1是让内核在SYN Flood发生时把连接信息加密编码进SYN-ACK的序号里不再依赖半连接队列。这个机制能保住正常用户但代价是TCP选项比如窗口缩放、时间戳在握手时会被弱化所以它更适合攻击时临时开启平时要看业务需求决定。tcp_syn_retries和tcp_synack_retries是控制SYN/SYN-ACK重传次数的。攻击场景下半连接是伪造的根本等不到回应重传只会让资源占用更久所以调成2大约几秒内就放弃。tcp_max_syn_backlog和somaxconn分别限制半连接队列和全连接队列的长度。调大后能扛住更大的瞬时连接突发但也不能无脑设成几十万队列太大会吃大量内存反而拖慢系统。常见服务器设置4096到8192就够用了。tcp_abort_on_overflow表示全连接队列满时直接向新连接回RST。它能让应用快速感知连接被拒而不是让用户一直卡在等待中。代价是客户端看到的是“连接被重置”但总比没有任何响应强。3.2 防火墙与流量限速规则iptables/nftables内核参数管的是协议栈本身防火墙管的是能不能让攻击包进入协议栈。下面一组iptables规则是我在实战里用得比较多的阈值要根据业务流量调整# 限制单个源IP的新建连接数超过直接丢 iptables -A INPUT -p tcp --syn -m connlimit --connlimit-above 200 --connlimit-mask 32 -j DROP # 按源IP限制SYN速率超过的丢包 iptables -A INPUT -p tcp --dport 443 --syn -m hashlimit --hashlimit-name https_syn \ --hashlimit-mode srcip --hashlimit-above 1000/sec --hashlimit-burst 2000 -j DROP # 丢弃状态异常的包 iptables -A INPUT -m state --state INVALID -j DROPconnlimit适合限制单IP发起的并发连接数。如果攻击者控制的肉鸡数量有限这个规则能直接卡死。但要注意NAT场景一个公司出口IP可能对应几百个真实用户阈值设太低会误杀。hashlimit比老牌的limit模块更适合做SYN速率限制因为limit是全局限速攻击流量一来正常流量会被一起限死hashlimit按源IP维度做哈希桶可以更公平地分配额度。上面这个例子限制单个源IP每秒最多1000个SYN超过的丢弃。到底设多少要按你日常流量基线来宁可先设得宽一点也不要上来就把正常请求误伤。INVALID状态匹配能丢一部分畸形包但非对称路由环境下有些正常包会被误判为INVALID所以这条规则上线前最好先跑一段日志模式观察。如果用的是nftables语法和iptables不同但思路一致。3.3 网络层清洗云高防、CDN与BGP黑洞本地调优和防火墙规则只能防住中小规模攻击。攻击流量超过单机带宽或者机房入口带宽时就是在给整个物理链路施压业务服务器上的任何配置都救不回来。这时候必须依赖上游的流量清洗能力。常见的做法有三种。第一种是BGP黑洞由运营商把被攻击IP的路由指向null0让流量直接丢弃。代价是这个IP的对外服务完全不可用所以只用于超大流量攻击时的“丢车保帅”。第二种是云清洗/高防IP。被攻击IP的流量先被牵引到清洗设备清洗设备通过特征库和行为分析把攻击包过滤掉再把正常流量回源。这套方案对同步/ACK Flood效果不错一般能扛几十G到几百G的流量。但前提是源站IP不能泄露否则攻击者绕过高防直接打源站IP清洗就形同虚设。第三种是CDN前置。把业务域名解析到CDN节点用户请求只打到CDN源站IP不暴露。动态业务用CDN要评估兼容性但至少能让SYN Flood这类四层攻击停在CDN边缘到不了源站。配了CDN之后一定要在源站防火墙做白名单只允许CDN回源IP访问源站端口否则源站IP暴露了还是白搭。3.4 业务层和应用架构层面的优化防护不只靠网络设备和防火墙业务侧也能配合降低攻击影响。TCP长连接和短连接的选择就是一个权衡点。短连接每次请求都要经过三次握手如果攻击者伪造SYN每一次握手失败都会消耗服务器资源长连接可以减少握手频率但连接一旦建立就会长时间占用状态表和内存反而成了“连接耗尽型”攻击的目标。我的建议是普通业务尽量用连接池复用限制单IP允许建立的连接数同时配合空闲超时把僵尸连接及时清掉。应用层的超时参数也要调。Nginx的keepalive_timeout、client_header_timeout、proxy_read_timeout如果设得太长在半连接异常风险下会有大量连接占着worker不释放设得太短正常网络抖动又会频繁断连。比较合理的做法是结合监控数据把超时调到正常请求P99耗时的3到5倍。尽量缩小暴露面只开放业务需要的端口管理端口通过堡垒机或白名单访问数据库、Redis这些内部服务绝不能把监听地址暴露到公网。很多DDoS攻击其实是先扫描端口再挑开放的端口来打少一个暴露端口就少一个被攻击的点。4. 实操案例一次SYN Flood应急响应实录4.1 现象与初步定位某个周五下午监控告警突然响个不停生产环境所有HTTPS接口的P99延迟从50毫秒涨到5秒以上。我第一反应是ssh到Nginx主机看状态结果ssh秒连说明攻击不是针对整体网络的。curl -I https://业务域名一直转圈最终超时。top里CPU整体不高但软中断si占了将近40%这在正常情况下很少见。接着执行ss -s看到TCP连接总数从平时的几千涨到了几十万其中SYN-RECV状态占了一大半。再翻dmesg有一行很显眼的东西possible SYN flooding on port 443. Sending cookies.。到这步基本可以断定打过来的是SYN Flood目标就是443端口。4.2 抓包确认攻击类型为了拿到更完整的证据我用tcpdump抓了10秒的包tcpdump -i eth0 -nn tcp port 443 and tcp[tcpflags] (tcp-syn|tcp-ack) ! 0 -c 10000 -w /tmp/attack.pcap抓完用tshark数了一下包tshark -r /tmp/attack.pcap -Y tcp.flags.syn1 and tcp.flags.ack0 | wc -l结果里纯SYN包占了九成以上源IP几乎没有重复包长度集中在64字节左右TTL值落在几个固定数字附近。这说明源IP大概率是伪造的或者来自同一类僵尸网络。正常的用户流量不可能一分钟内出现几万个不同源IP而且全部只发SYN不发ACK。攻击类型确认无误开始处置。4.3 分层处置过程处置顺序很关键我当时分了四步。第一步先在服务器本地把内核参数顶上去。因为syncookies已经在触发但配置还没显式生效。执行了sysctl -w net.ipv4.tcp_syncookies1 sysctl -w net.ipv4.tcp_max_syn_backlog8192 sysctl -w net.core.somaxconn8192 sysctl -w net.ipv4.tcp_synack_retries1执行后ss -tan state syn-recv的数量肉眼可见地往下走但还没有完全恢复正常。第二步上iptables限速规则。针对443端口的纯SYN包做了hashlimit限制又用connlimit把单IP并发连接数限制在500。这个阈值在当时是安全的因为我们这个业务正常用户单IP并发不会超过几十。第三步联系云厂商开启高防清洗。当时流量已经到了8Gbps虽然带宽还没完全打满但本地CPU已经撑不住了。流量切到清洗节点后回源只放行清洗后的正常流量。同时我在源站Nginx上加了白名单只允许高防回源IP访问源站443端口防止攻击者绕过高防直接打源站。第四步业务恢复后临时调整了Nginx的proxy_read_timeout从原来的10秒调到30秒。原因是复盘时发现攻击切换清洗的瞬间部分回源连接被重置超时太短导致正常请求也被打断。整体大约1小时攻击自然停止。事后看监控PPS峰值大约500万持续了将近40分钟。4.4 复盘与加固清单打完这一仗我做了一份加固清单后面再遇到类似问题能省很多时间。内核参数写入/etc/sysctl.conf持久化重启不丢。iptables规则写成启动脚本并测试过规则顺序确保白名单在限速之前匹配。监控维度里增加半连接数、syncookies发送计数、PPS、SYN包速率这几个指标。很多人只看带宽和CPU忽略PPS结果攻击来了还一脸懵。高防、清洗、源站白名单要提前和云厂商对接别等到被打才去建工单。平时就做好高防IP和源站IP的绑定测试。每个季度做一次小规模演练模拟半连接暴涨时业务的降级方案。比如静态页面切CDN、非核心接口熔断、临时关闭重端口。至少得知道真出事的时候谁负责联系运营商谁负责改防火墙规则。5. 常见问题与排查技巧5.1 误杀正常连接怎么办防护规则最怕的就是误杀。connlimit的阈值如果设得太低一个公司NAT出口的IP可能被整个封掉内部几百人同时打不开页面投诉电话会被打爆。我习惯的做法是所有限速规则先上线到日志模式观察比如一天看正常IP有没有被匹配到。如果日志里出现已知的监控源IP或合作方IP说明阈值太紧要再放大。阈值通常按日常峰值的3到5倍起步确认没有误杀之后再逐步下调到安全线。对重要的合作方、办公网出口IP单独加一条白名单ACCEPT规则放在DROP规则前面避免被误伤。5.2 SYN Cookie开启后性能下降SYN Cookie是攻击时救急用的但它确实会带来性能副作用。开启后握手信息被编码在SYN-ACK的序列号里TCP窗口缩放、时间戳这些扩展选项带不了高带宽长连接的传输性能会受影响。如果业务是大文件下载、视频点播这类对吞吐敏感的场景长期开着syncookies不合适。更好的方案是让攻击流量在前端清洗设备被挡住后端服务器只在紧急时临时开启syncookies。如果攻击规模到不了需要清洗设备的程度但又频繁触发SYN Flood可以在入口处用SYNPROXY这类无状态代理来做握手代答后端服务器不直接暴露在攻击路径上。5.3 服务器仍收到大量半连接如果已经开了syncookies也调大了队列ss里还是躺着大量SYN-RECV先别急着继续调参检查两件事。第一应用进程listen的backlog是否真的够大。如果程序里写死listen(80, 128)那内核队列调成4096也没用实际生效的还是128。Nginx这种可以显式配置listen 443 backlog4096改完要reload。第二攻击是否打到了前面的LB或防火墙根本没到后端服务器。四层负载均衡LVS、DPVS上如果看到大量SYN半连接而后端是正常的那就说明攻击被LB扛住了但LB的状态表可能会被耗尽。这时候要在LB前面加清洗而不是在后端主机上反复调内核。5.4 长连接场景下的ACK Flood应对TCP长连接场景下ACK Flood尤其难防。因为长连接本身就会有持续的ACK流量你要是单纯对ACK限速下载服务、消息推送立刻就被自己搞挂了。我的经验是把重心从“限制ACK包数量”换成“无效连接快速回收”。连接跟踪表conntrack是ACK Flood容易打满的地方。可以通过修改net.netfilter.nf_conntrack_tcp_timeout_established把已建立连接的超时从默认的432000秒约5天缩短到600秒让长时间没数据的连接更快被回收。再结合应用层心跳如果心跳超时服务端主动断开连接能有效防止攻击者通过大量低频ACK占住连接表。需要注意缩短conntrack超时会影响慢速但正常的连接比如某些工控设备、长轮询接口。上线前必须对业务做充分测试避免正常连接被提前回收。5.5 常用排查命令速查最后把这次应急里用到的命令整理一下方便直接抄作业。# 查看当前TCP状态统计 ss -s # 查看半连接数量 ss -tan state syn-recv | wc -l # 查看已建立的连接数 ss -tan state established | wc -l # 查看内核syncookies计数 netstat -s | grep -i syncookies # 抓取指定端口TCP包并保存 tcpdump -i eth0 -nn tcp port 443 -c 10000 -w /tmp/attack.pcap # 统计纯SYN包 tcpdump -r /tmp/attack.pcap -nn tcp[tcpflags] tcp-syn ! 0 and tcp[tcpflags] tcp-ack 0 | wc -l这些命令在攻击发生时多跑几遍比开一堆监控面板更直观。但要注意抓包本身有额外开销在已经高负载的服务器上抓包要控制时长和包数别把生产机器抓挂了。这次处理完之后我把所有入口主机的半连接数、SYN速率和syncookies发送计数都加到了监控面板里平时看起来有点多余但真出事的时候这几个数字能让你少走至少半小时弯路。另外再说一个容易被忽略的点很多DDoS并不是突然打到天量在爆发前会有小规模探测比如少数IP反复发SYN到不同端口把这些探测日志收集起来提前封禁很多时候能直接压掉一场正在预谋的攻击。希望这篇能帮到正在和TCP流量死磕的你。
返回列表