SYN Flood攻击之所以难防,核心在于它利用了TCP三次握手的协议缺陷,通过发送大量伪造源IP的SYN包,迅速填满服务器的半连接队列(SYN Queue),导致正常请求被丢弃。解决这个问题的关键不是盲目加大队列长度,那样只会消耗更多内存并增加延迟,而是要从内核层面缩短半连接状态的超时时间、减少重试次数,并在必要时触发SYN Cookie机制来彻底绕过队列限制。

理解半连接队列与SYN Cookie的博弈

当服务器收到一个SYN包,它会将该连接信息存入半连接队列,并回复SYN-ACK,等待客户端的ACK确认。如果这个ACK迟迟不来,内核会根据tcp_synack_retries参数进行重传。默认情况下,这个值是5,意味着在长达数分钟的时间里,这个半连接状态会一直占用队列资源。攻击者正是利用这段时间,用海量的虚假SYN包撑爆队列。调整重试次数是止血的第一步,但更彻底的方案是启用并优化SYN Cookie。SYN Cookie是一种无状态防御机制,当半连接队列满时,内核不再分配新的内存去存储连接,而是将必要的连接信息加密编码进序列号中发回给客户端。如果客户端是真实的,它会带着这个特殊的Cookie回来,内核解密验证后直接建立连接,完全不需要占用半连接队列。

核心参数深度调优:从重试次数到队列压力

直接修改/etc/sysctl.conf文件是实施防御的起点。首先要动刀的是tcp_synack_retries。将其从默认的5降低到1或2,这意味着服务器只会重试1到2次SYN-ACK,半连接状态的存活时间从分钟级骤降到秒级。对于高并发Web服务器,建议直接设为1。对应的命令是:

net.ipv4.tcp_synack_retries = 1

接下来是tcp_syn_retries,它控制客户端发起连接时SYN包的重试次数,在服务器作为客户端连接外部服务时起作用,降低它也能减少出站资源的浪费,建议设为2:

net.ipv4.tcp_syn_retries = 2

真正决定何时启用SYN Cookie的是tcp_syncookies参数。在较新的内核中,它不仅仅是一个开关。设为1时,仅在半连接队列满时触发;设为2时,内核会无条件启用SYN Cookie,这是一种更激进但更安全的策略,尤其适合始终暴露在DDoS威胁下的核心服务器。建议直接设为2:

net.ipv4.tcp_syncookies = 2
队列大小的艺术:不再盲目追求大容量

很多人误以为把tcp_max_syn_backlog调得越大越好,比如设为65535。这是一个危险的误区。半连接队列的实际大小并非由此单一参数决定,它受到somaxconn和应用程序listen时指定的backlog共同约束。内核实际使用的半连接队列上限是min(tcp_max_syn_backlog, somaxconn)。更关键的是,队列中每个等待的条目虽然比完全建立的连接占用内存小,但数量巨大时依然会耗尽内存,且队列遍历效率会急剧下降。合理的做法是设置一个中等偏小的值,如8192,配合极短的超时时间和SYN Cookie,让队列迅速周转,而不是变成一个巨大的僵尸池。设置如下:

net.ipv4.tcp_max_syn_backlog = 8192
net.core.somaxconn = 8192

同时,务必在应用程序的listen调用中,将backlog参数也设置为8192或更高,比如Nginx的listen指令的backlog参数。

时间等待与连接回收的协同防御

SYN Flood不仅攻击半连接队列,大量的虚假连接尝试也会产生TIME_WAIT状态,占用端口资源。优化tcp_tw_reuse和tcp_fin_timeout能加速资源的回收。tcp_tw_reuse允许将处于TIME_WAIT状态的连接用于新的出站连接,对于承受攻击后需要快速恢复服务能力的服务器至关重要。tcp_fin_timeout控制FIN_WAIT_2状态到TIME_WAIT的超时时间,默认60秒,可降至30秒。但请注意,tcp_tw_recycle在较新内核中已被移除,因为它对NAT环境下的时间戳检查会导致严重问题,切勿尝试。相关优化如下:

net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30

此外,tcp_abort_on_overflow是一个容易被误解的参数。当全连接队列(Accept Queue)满时,如果设为1,服务器会直接发送RST包复位连接。这听起来能快速清理连接,但对于正常的高并发突发流量,这会导致客户端看到“connection reset”错误。在防御SYN Flood的场景下,全连接队列满往往是攻击成功的标志,此时发送RST可能会让攻击者得知攻击生效,且误伤可能存在的少量正常用户。因此,除非能精确识别攻击流量,否则建议保持默认的0,让服务器直接丢弃包,让客户端进行重试。

利用iptables结合内核参数实现速率限制

内核参数调优是基础防线,但面对流量型攻击,必须在网络层进行速率限制。iptables的limit和hashlimit模块能完美配合内核参数工作。通过限制每个源IP单位时间内的SYN包数量,可以在攻击流量到达应用层之前就将其丢弃,保护半连接队列。以下规则限制每个IP每秒最多发送10个SYN包,超过的将被丢弃,并记录日志:

iptables -N SYN_FLOOD
iptables -A INPUT -p tcp --syn -j SYN_FLOOD
iptables -A SYN_FLOOD -m limit --limit 10/s --limit-burst 20 -j RETURN
iptables -A SYN_FLOOD -j LOG --log-prefix "SYN Flood: "
iptables -A SYN_FLOOD -j DROP

对于更复杂的场景,hashlimit可以基于源IP或网络段进行更精细的带宽和包速率控制。例如,对每个/24网段进行限制,防止分布式攻击:

iptables -A INPUT -p tcp --syn -m hashlimit --hashlimit-name synflood --hashlimit-above 50/sec --hashlimit-burst 100 --hashlimit-mode srcip --hashlimit-srcmask 24 -j DROP

这种网络层的速率限制与内核的快速超时重试机制相结合,形成了纵深防御。攻击流量在第一步就被大量拦截,即使有漏网之鱼,内核也会因为极短的tcp_synack_retries和强制的SYN Cookie而无法形成有效队列堆积。

监控与验证:确保调优真正生效

调优不是一劳永逸的,必须通过监控来验证。使用ss -s命令可以快速查看当前系统TCP连接的整体统计,重点关注半连接状态的数值。持续使用watch命令观察:

watch -n 1 'ss -s'

更细粒度的监控需要查看/proc/net/snmp和/proc/net/netstat中的具体字段。例如,TcpAttemptFails和TcpExtListenOverflows能直接反映连接失败和队列溢出的情况。如果TcpExtListenOverflows持续增长,说明全连接队列有溢出,需要调整somaxconn和应用的backlog。而TcpExtSyncookiesSent和TcpExtSyncookiesRecv则直接告诉你SYN Cookie机制的触发频率。如果SyncookiesSent数值很高,说明你的系统正在承受大量的SYN攻击,但SYN Cookie正在有效工作。如果SyncookiesRecv远低于SyncookiesSent,说明大部分攻击源是伪造的,无法完成三次握手,你的防御策略是成功的。

进阶思路:TCP Fast Open与连接迁移

在极致性能与安全并存的场景下,可以考虑启用TCP Fast Open(TFO)。TFO允许在初始SYN包中就携带数据,对于合法的客户端,这能减少一次往返时延。从安全角度看,TFO使用Cookie机制,与SYN Cookie有异曲同工之妙,能一定程度上验证客户端的真实性。启用TFO需要内核和应用程序同时支持,内核参数设置为3(客户端和服务端均启用):

net.ipv4.tcp_fastopen = 3

这并非直接防御SYN Flood的手段,但它改变了连接的建立方式,使得攻击者更难用简单的SYN包消耗服务器资源。此外,对于超大型分布式系统,内核层面的调优只是单机防御。真正的海量攻击需要结合Anycast、ECMP等网络架构技术,将攻击流量分散到多个节点,每个节点都执行上述的内核加固策略,从而将攻击影响降至最低。

总结调优清单

将所有关键的防御参数整合,形成一个可直接应用的配置片段。这套配置假设服务器直接面对互联网,需要最严格的防御策略。应用前请根据业务特性,在测试环境充分验证。

# /etc/sysctl.conf 防御SYN Flood核心配置
net.ipv4.tcp_synack_retries = 1
net.ipv4.tcp_syn_retries = 2
net.ipv4.tcp_syncookies = 2
net.ipv4.tcp_max_syn_backlog = 8192
net.core.somaxconn = 8192
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_fastopen = 3
net.ipv4.tcp_abort_on_overflow = 0

执行sysctl -p使其立即生效。记住,这些参数是相互协作的,单独调整某一个很难达到理想效果。缩短重试时间、强制启用SYN Cookie、控制队列大小、加速资源回收,并结合iptables的速率限制,这五步构成了一个闭环的防御体系,让SYN Flood攻击在你的服务器面前确实无从下手。