1 SYN 洪泛攻击防御
1.1 SYN Flood是什么?
SYN Flood是互联网上最原始、最经典的DDoS(Distributed Denial of Service,分布式拒绝服务)攻击之一,旨在耗尽可用服务器资源,致使服务器无法传输合法流量。
SYN Flood利用了TCP协议的三次握手机制(具体了解可以查看这一篇从DNS到TCP:深入解析网络通信的全过程(包含DNS解析流程和浏览器输入域名访问整个流程)-CSDN博客),攻击者通常利用工具或者控制僵尸主机向服务器发送海量的变源IP地址或变源端口的TCP SYN报文,服务器响应了这些报文之后就会生成大量的半连接,当系统资源被耗尽后,服务器将无法提供正常的服务。
增加服务器性能,提供更多的连接能力对于SYN的海量报文来说杯水车薪,防御SYN Flood的关键在于判断哪些连接请求来自于真实源,屏蔽非真实源的请求以保障正常的业务请求能得到服务。
1.2 TCP SYN Flood攻击原理是什么?
TCP SYN Flood 攻击利用的是TCP的三次握手(SYN->SYN/ACK->ACK),假设连接发起方是A,连接接收方是B,即B在某个端口(Port)上监听A发出的连接请求,过程如下图所示,左边是A,右边是B。
A首先发送 SYN(Synchronization)消息给B,要求B做好接收数据的准备;B收到后反馈SYN-ACK(Synchronization-Acknowledgement)消息给A,这个消息的目的有两个:
- 向 A 确认已做好接收数据的准备,
- 同时要求 A 也做好接收数据的准备,此时 B 已向 A 确认好接收状态,并等待 A 的确认,连接处于半开状态(Half-Open),顾名思义只开了一半;A 收到后再次发送 ACK (Acknowledgement) 消息给 B,向 B 确认也做好了接收数据的准备,至此三次握手完成,「连接」就建立了,
最关键的一点在于双方是否都按照对方的要求进入了可接受消息的状态。而这个状态的确认主要是双方将要使用的消息序号(SequenceNum),TCP为保证消息按照发送顺序抵达接收方的上层应用,需要用消息序号来标记信息发送的先后顺序。
TCP是【双工】(Duplex)连接,同时支持双向通信,也就是双方同时可以向对方发送消息,其中 SYN 和 SYN--ACK 消息开启了A->B的单向通信通道(B获知了A的消息序号);SYN-ACK 和 ACK 消息开启了B->A单向通信通道(A获知了B的消息序号)。
上面讨论的是双方在诚实守信,正常情况下的通信。但实际情况是网络可能不稳定会丢包,使握手消息不能抵达对方,也可能是对方故意不按照规矩来,故意延迟或不发送握手确认消息。
假设B通过某 TCP 端口提供服务,B再接收到A的 SYN 消息的时候,积极地反馈了SYN-ACK消息,使连接进入半开状态,因为B不确定自己发给A的SYN-ACK消息或A反馈的ACK消息是否会丢在半路,所以会给每个待完成的半开连接都设置一个Timer,如果超过时间还没有收到A的ACK消息,则重新发送一次SYN-ACK消息给A,直到重试超过一定次数才会放弃。
B为了帮助A能顺利链接,需要分配内核资源维护半开连接,那么当B面临海量的连接A时,如上图所示,SYN Flood攻击就形成了。攻击方A可以控制肉鸡向B发送大量的SYN消息但不响应ACK消息,或者干脆伪造SYN消息中的Source IP,使B反馈的SYN-ACK消息石沉大海,导致B被大量注定不能完成的半开连接占据,直到资源耗尽,停止响应正常的连接请求。
“控制肉鸡”是指攻击者通过恶意软件(如病毒、蠕虫、木马等)感染并控制大量他人的计算机或设备。这些被控制的设备被称为“肉鸡”或“僵尸机”,它们组成了“僵尸网络”(Botnet)。攻击者可以远程操纵这些设备,利用它们发起大规模的网络攻击,如DDoS攻击、垃圾邮件发送等。
在DDoS攻击中,攻击者通过控制肉鸡向目标服务器发送大量请求,耗尽目标资源,使其无法响应正常用户的请求。例如,攻击者可以让肉鸡发送大量SYN消息但不回应ACK消息,或者伪造SYN消息的源IP地址,导致目标服务器等待永远不会到达的ACK消息,最终资源耗尽,停止服务。
1.3 SYN Flood 的常见形式有哪些?
恶意用户可通过三种不同方式发起 SYN Flood 攻击:
- 直接攻击: 不伪造 IP 地址的 SYN 洪水攻击称为直接攻击。在此类攻击中,攻击者完全不屏蔽其 IP 地址。由于攻击者使用具有真实 IP 地址的单一源设备发起攻击,因此很容易发现并清理攻击者。为使目标机器呈现半开状态,黑客将阻止个人机器对服务器的 SYN-ACK 数据包做出响应。为此,通常采用以下两种方式实现:部署防火墙规则,阻止除 SYN 数据包以外的各类传出数据包;或者,对传入的所有 SYN-ACK 数据包进行过滤,防止其到达恶意用户机器。实际上,这种方法很少使用(即便使用过也不多见),因为此类攻击相当容易缓解 – 只需阻止每个恶意系统的 IP 地址。哪怕攻击者使用僵尸网络(如 Mirai 僵尸网络),通常也不会刻意屏蔽受感染设备的 IP。
- 欺骗攻击: 恶意用户还可以伪造其发送的各个 SYN 数据包的 IP 地址,以便阻止缓解措施并加大身份暴露难度。虽然数据包可能经过伪装,但还是可以通过这些数据包追根溯源。此类检测工作很难开展,但并非不可实现;特别是,如果 Internet 服务提供商 (ISP) 愿意提供帮助,则更容易实现。
- 分布式攻击(DDoS): 如果使用僵尸网络发起攻击,则追溯攻击源头的可能性很低。随着混淆级别的攀升,攻击者可能还会命令每台分布式设备伪造其发送数据包的 IP 地址。哪怕攻击者使用僵尸网络(如 Mirai 僵尸网络),通常也不会刻意屏蔽受感染设备的 IP。
1.4 如何缓解 SYN Flood ?
1.4.1 扩展积压工作队列
目标设备安装的每个操作系统都允许具有一定数量的半开连接。若要响应大量的SYN数据包,一种方法是增加操作系统允许的最大半开连接数目。为成功扩展最大积压工作,系统都必须额外预留内存资源以处理各类新请求。如果系统没有足够的内存,无法应对增加的挤压工作队列规模,将对系统性能产生负面影响,但仍然好过拒绝服务。
1.4.2 回收最先创建的TCP半开连接
另一种缓解策略是在填充挤压工作后覆盖最先创建的半开连接。这项策略要求完全建立合法连接的时间低于恶意SYN数据包填充挤压工作的时间。当攻击量增加或挤压工作规模小于实际需求时,这项特定的防御措施将不奏效。
1.4.3 SYN Cookie
此策略要求服务器创建Cookie。为避免在填充积压工作时断开连接,服务器使用SYN-ACK数据包响应每一项连接请求,而后从积压工作中删除SYN请求,同时从内存中删除请求,保证端口保持打开状态并做好重新建立连接的准备。如果连接是合法请求并且已将最后一个ACK数据包从客户端机器发回服务器,服务器将重建(存在一些限制)SYN积压工作队列条目。虽然这项缓解措施势必会丢失一些TCP连接信息,但好过因此导致对合法用户发起拒绝服务攻击。
2 TCP连接故障排查
2.1 SYN重传(SYN Retransmissions)问题
2.1.1 抓包深度分析
-
Wireshark筛选:
tcp.flags.syn == 1 && tcp.flags.ack == 0
,观察SYN包的Sequence Number
是否重复。 -
重传间隔:SYN重传遵循指数退避(Exponential Backoff),首次重传间隔通常为1s,后续为3s、7s等。若间隔异常,可能为内核参数被修改。
-
内核队列状态:
-
SYN队列(半连接队列):
ss -nltp
的SYN-RECV
状态计数。 -
Accept队列(全连接队列):
ss -lnt
的Recv-Q
列显示积压数(需对比net.core.somaxconn
配置)。
-
2.1.2 内核参数与队列溢出
# 查看SYN队列溢出统计
netstat -s | grep -i "SYNs to LISTEN"
# 查看Accept队列溢出(全连接队列满)
netstat -s | grep -i "times the listen queue of a socket overflowed"
根因与解决方案
-
队列溢出:
-
调整SYN队列长度
sysctl -w net.ipv4.tcp_max_syn_backlog=8192
-
调整Accept队列长度:
sysctl -w net.core.somaxconn=65535 # 应用层需配合(如Nginx的backlog参数): listen 80 backlog=65535;
-
启用SYN Cookie(防御洪水攻击):
sysctl -w net.ipv4.tcp_syncookies=1
-
-
网络路径问题:
-
MTU/MSS协商:检查中间设备是否强制分片(如VPN隧道),确保MSS值合理:
# 抓包验证MSS值(通常MSS = MTU - 40) tcpdump -i eth0 'tcp[tcpflags] & (tcp-syn) != 0 and tcp[tcpflags] & (tcp-ack) == 0'
-
路径MTU发现(PMTUD)失败:若ICMP "Fragmentation Needed" 被屏蔽,会导致黑洞路由:
sysctl -w net.ipv4.tcp_mtu_probing=2 # 启用主动MTU探测
-
2.2 数据包重传(Retransmissions)
2.2.1 区分重传类型
-
超时重传(Timeout Retransmission):RTO(Retransmission Timeout)触发,通常因网络严重拥塞。
-
快速重传(Fast Retransmit):收到3个重复ACK后触发,表明单包丢失。
-
乱序重传(Spurious Retransmission):因网络乱序误判丢包(需结合SACK分析)。
2.2.2 RTO计算与内核参数
- RTO动态计算:基于RTT(Round Trip Time)和RTTVAR(RTT Variation)。
- RTO最小/最大值:
sysctl -w net.ipv4.tcp_rto_min=200 # 最小RTO(默认1s)
sysctl -w net.ipv4.tcp_rto_max=60000 # 最大RTO(默认120s)
2.2.3 拥塞控制算法选择
-
CUBIC:默认算法,适合高带宽延迟积(BDP)网络。
-
BBR(Bottleneck Bandwidth and RTT):避免Bufferbloat,提升吞吐:
sysctl -w net.ipv4.tcp_congestion_control=bbr
-
验证算法生效:
sysctl net.ipv4.tcp_congestion_control
根因与解决方案
-
链路丢包:
-
物理层检测:使用
ethtool
检查网卡错误计数:ethtool -S eth0 | grep -E 'rx_errors|tx_errors'
-
交换机排查:检查端口CRC错误、队列丢弃:
# Cisco交换机示例 show interface GigabitEthernet0/1 | include errors|drops
-
-
拥塞控制优化:
-
BBR参数调优(Linux 4.19+):
sysctl -w net.ipv4.tcp_bbr_bw_rtt_compensation=1 # 更激进带宽探测 sysctl -w net.ipv4.tcp_bbr_probe_rtt_cwnd_gain=1 # 降低ProbeRTT阶段CWND
-
2.3 重复ACK(Duplicate ACKs)
2.3.1 SACK(Selective Acknowledgment)分析
-
抓包过滤:
tcp.options.sack
,观察接收方是否通过SACK反馈丢失的序列号范围。 -
内核支持:确保SACK启用:
sysctl -w net.ipv4.tcp_sack=1
2.3.2 D-SACK(Duplicate SACK)检测
-
作用:检测虚假重传(Spurious Retransmissions)。
-
启用条件:需同时启用SACK和D-SACK:
sysctl -w net.ipv4.tcp_dsack=1
根因与解决方案
-
接收方处理延迟:
-
应用层缓冲优化:使用零拷贝技术(如Linux的
sendfile
系统调用)。 -
调整SO_RCVBUF:
// 代码示例:设置接收缓冲区大小 int rcvbuf_size = 1024 * 1024; setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &rcvbuf_size, sizeof(rcvbuf_size));
-
2.4 RST包激增(RST Storms)
2.4.1 上下文关联分析
-
连接状态机冲突:检查RST包前后数据包,判断是否因状态不一致触发(如收到FIN后仍发送数据)。
-
内核日志:
dmesg
中可能记录TCP栈异常(如TCP: Treason uncloaked!
)。
2.4.2 TIME-WAIT状态优化
-
重用TIME-WAIT连接:
sysctl -w net.ipv4.tcp_tw_reuse=1 # 允许作为客户端重用 sysctl -w net.ipv4.tcp_tw_recycle=1 # 注意:Linux 4.12+已废弃此参数
-
调整TIME-WAIT超时:
sysctl -w net.ipv4.tcp_fin_timeout=30 # 默认60s
根因与解决方案
-
应用层协议错误:
-
HTTP Keep-Alive超时不一致:确保客户端和服务端
Keep-Alive
超时配置匹配。 -
SSL/TLS握手失败:检查证书链完整性和协议版本兼容性(如TLS 1.2 vs TLS 1.3)。
-
2.5 零窗口(Zero Window)
2.5.1 接收窗口动态调整
-
TCP Window Scaling:支持窗口缩放因子(最大65535 * 2^14):
sysctl -w net.ipv4.tcp_window_scaling=1
-
Auto-Tuning机制:动态调整接收窗口(需关闭限速):
sysctl -w net.ipv4.tcp_moderate_rcvbuf=1
2.5.2 应用层阻塞分析
-
Strace跟踪:检查应用是否在
recv()
系统调用阻塞:strace -p <PID> -e trace=recvfrom,sendto
-
Off-CPU分析:使用
eBPF
工具(如offcputime
)定位线程阻塞点。
根因与解决方案
-
内存压力:
-
Direct Memory Reclaim:监控
/proc/vmstat
的pgsteal_kswapd_*
和pgscan_kswapd_*
,调整vm.swappiness。 -
NUMA优化:绑定进程到特定NUMA节点,减少跨节点访问延迟。
-
2.6 总结
-
分层诊断:从物理层(网卡错误)→ 网络层(路由/防火墙)→ 传输层(TCP参数)→ 应用层(协议设计)逐层排查。
-
动态调优:结合实时监控(Prometheus + Grafana)与自动化工具(Ansible/Terraform)实现参数动态调整。
-
协议栈定制:在极端性能场景下,可考虑修改内核TCP协议栈(如Facebook的Katran负载均衡器)。
通过上述专业方法,可精准定位复杂TCP问题的根因,并实现高性能、高可靠的网络通信。