TCP的SYN Flood攻击与SYN Cookie防护机制深度详解
1. 问题描述/知识点介绍
SYN Flood是一种常见的分布式拒绝服务(DDoS)攻击手段,它利用了TCP协议三次握手过程中的一个设计缺陷。攻击者通过发送大量伪造源IP地址的TCP SYN报文(即连接请求报文),消耗目标服务器的资源(主要是半连接队列),导致服务器无法为正常用户建立新的TCP连接,从而达到拒绝服务的目的。为了抵御这种攻击,SYN Cookie机制被提出并广泛应用。
2. 攻击原理深度解析:为什么SYN Flood有效?
要理解攻击,必须先理解正常的三次握手和服务器资源分配:
- 正常握手:
- 客户端发送SYN报文给服务器。
- 服务器收到SYN后:必须为该潜在连接分配内核资源。这包括:① 创建并初始化一个传输控制块(TCB, Transmission Control Block),用于存储连接的状态信息(如序列号、窗口大小等);② 将该连接放入“半连接队列”(SYN Queue)。然后,服务器回复SYN-ACK。
- 客户端回复ACK,连接建立,TCB从半连接队列移到“全连接队列”(Accept Queue),等待应用层
accept()调用。
- 攻击过程:
攻击者发送大量的SYN报文,并且伪造源IP地址(使其指向一个不可达或不存在的主机)。- 服务器收到每个伪造的SYN,都会诚实地执行上述步骤:分配TCB资源,并将其放入半连接队列。
- 服务器发送SYN-ACK到伪造的IP地址,并等待对方的ACK。
- 由于源IP是伪造的,ACK永远不会到来(或者到来的RST报文不是服务器所期望的)。
- 服务器会持续重传SYN-ACK(通常有次数限制,如5次),并等待一个超时时间(如30秒、1分钟)后,才销毁这个半连接,释放资源。
- 关键资源耗尽:
- 半连接队列(SYN Queue)大小有限。一旦队列被这些“僵尸”半连接占满,服务器将不再接受任何新的SYN请求,即使来自合法用户。
- 内核内存和CPU 被大量无用的TCB占用。
3. 传统防御方案的局限性
在SYN Cookie出现前,常见的缓解措施有:
- 增加半连接队列大小:治标不治本,只是延缓被填满的时间,且消耗更多内存。
- 缩短SYN-ACK重传超时时间:让“僵尸”连接更快被清理,但会影响在高延迟网络下的正常连接建立。
- 使用防火墙或入侵检测系统(IDS)进行SYN代理或过滤:这需要额外的中间设备,可能成为性能瓶颈,且对于海量伪造IP的攻击,过滤规则难以生效。
4. SYN Cookie机制的核心理念与目标
SYN Cookie的核心思想是:在完成三次握手之前,服务器不分配任何存储资源来维护连接状态。它将连接的必要信息“编码”到初始序列号(ISN)中,作为SYN-ACK报文的一部分发送出去。只有在收到合法的ACK(即第三次握手)时,才利用ACK报文中的信息“解码”并重建连接状态。这实现了 “无状态”的握手协商。
5. SYN Cookie的工作机制(详细步骤)
阶段一:接收SYN时(无状态分配)
- 服务器收到SYN报文。
- 服务器不分配TCB,也不将连接信息放入半连接队列。
- 服务器根据SYN报文中的信息,计算一个特殊的初始序列号,称为“Cookie”。计算通常使用一个哈希函数,输入值包括:
- 源/目的IP地址和端口号(四元组,唯一标识一个连接)。
- 一个只有服务器知道的秘密值(Secret):定期(如每5分钟)更换,防止攻击者预测或伪造Cookie。
- 一个递增的时间戳或计数器(t):通常以固定的时间单位(如64秒)递增,用于防止Cookie被无限期重用。
公式简化为:Cookie = Hash(Secret, Src_IP, Src_Port, Dst_IP, Dst_Port, t) mod 2^32
这个Cookie值将作为服务器在SYN-ACK报文中的 ISN(Initial Sequence Number) 发送给客户端。
阶段二:发送SYN-ACK
服务器构造SYN-ACK报文,其中的确认号(ACK Num)是客户端的初始序列号+1,而自己的序列号(SEQ Num)就是计算出的Cookie值。发送此报文后,服务器不保留任何关于此连接的信息。
阶段三:接收ACK并验证Cookie(状态重建)
- 合法的客户端会回复ACK报文。ACK报文中包含:
- 确认号(ACK Num):等于服务器的ISN(即Cookie值)+ 1。
- 自己的序列号(SEQ Num):等于其最初的初始序列号+1(对于ACK报文,这个值通常不重要,但协议规定存在)。
- 服务器收到ACK后,需要进行Cookie验证:
a. 从ACK报文的确认号中提取出对方确认的序列号:Received_Cookie = ACK_Num - 1。
b. 根据ACK报文的源/目的IP端口,以及当前时间(或最近几个时间单位的t值),使用相同的哈希函数和秘密值重新计算一个Cookie值:Calculated_Cookie = Hash(Secret, Src_IP, Src_Port, Dst_IP, Dst_Port, t‘) mod 2^32。这里的t‘可能尝试当前时间和前几个时间单位的值,以补偿网络延迟。
c. 比较:如果Received_Cookie等于Calculated_Cookie(或在一定误差范围内),则验证通过。否则,ACK报文是伪造的或过期的,服务器直接丢弃。 - 验证通过后的操作:
- 此时,服务器确信对方是能够收到SYN-ACK的真实客户端(因为伪造IP的主机收不到SYN-ACK,也就无法回复包含正确Cookie的ACK)。
- 服务器此时才分配TCB,根据Cookie中可能编码的有限信息(如MSS,通常MSS信息会被编码在Cookie值的低几位中)和ACK报文中的信息,初始化连接状态。
- 连接进入ESTABLISHED状态,并放入全连接队列,等待应用层处理。
- 如果ACK验证失败:服务器不做任何处理,没有资源被消耗。
6. SYN Cookie的优缺点分析
-
优点:
- 彻底免疫SYN Flood:攻击者发送再多的SYN,服务器也不会分配内存资源。
- 完全兼容:对合法客户端透明,客户端无需任何修改。
- 无需复杂配置:是现代操作系统内核的内置功能。
-
缺点:
- 无法支持所有TCP选项:因为第一次握手(SYN)中的TCP选项信息(如窗口缩放因子、时间戳、SACK允许等)在无状态阶段无法保存。这些选项通常被用于性能优化。服务器在SYN-ACK中虽然可以包含这些选项,但在收到ACK重建状态时,无法获知客户端最初在SYN中通告的支持能力。因此,SYN Cookie机制下建立的连接,可能无法使用某些高级TCP选项,从而牺牲了一些性能(例如,可能无法使用窗口缩放,限制了最大窗口大小)。
- Cookie计算带来轻微CPU开销:每次握手都需要计算和验证哈希,但相对于内存耗尽,这个开销是完全可以接受的。
- 时间同步要求:Cookie中的时间计数器要求服务器时钟基本稳定,网络延迟不能过大,否则可能导致合法客户端的ACK因Cookie过期而被拒绝。
7. 实践与启用
在Linux系统中,可以通过/proc/sys/net/ipv4/tcp_syncookies文件控制:
0: 禁用。1: 仅在半连接队列即将溢出时启用。2: 无条件启用。
通常设置为1,在遭受攻击时自动启用保护,平时则使用完整的TCP选项以获得最佳性能。
总结:SYN Cookie是一种巧妙的“空间换时间/性能”的权衡策略。它通过将连接状态信息编码到序列号中并延迟资源分配,以牺牲部分连接性能优化特性为代价,换取了服务器在面对SYN Flood攻击时强大的生存能力,是TCP/IP协议栈安全加固中的一个经典设计。