SYN泛洪攻击
什么是 SYN 泛洪攻击?
SYN 洪水(半开连接攻击)是一种拒绝服务 (DDoS) 攻击 ,旨在耗尽可用服务器资源,致使服务器无法传输合法流量。通过 TCP 三次握手时,重复发送初始连接请求 (SYN) 数据包,攻击者将占满目标服务器上的所有 半连接队列 ,导致服务器在响应合法流量时表现迟钝乃至全无响应。
SYN 泛洪攻击原理?
- 攻击者通常使用伪造的不同的 IP 地址向目标服务器发送大量
SYN
数据包。 - 然后,服务器分别对每一项连接请求做出响应(发送
ACK+SYN
包),并确保打开的端口做好接收响应的准备。 - 在服务器等待最后一个
ACK
数据包(永远不会到达)的过程中,攻击者将继续发送更多SYN
数据包,久而久之就会占满服务端各个端口的 半连接队列 ,使得服务器不能为正常用户服务。
半连接队列与全连接队列
正常流程:
- 当服务端接收到客户端的
SYN
报文时,会将其加入到内核的 SYN 队列(半连接队列) ; - 接着发送
SYN + ACK
给客户端,等待客户端回应ACK
报文; - 服务端接收到
ACK
报文后,从 SYN 队列移除放入到 Accept 队列(全连接队列) ; - 应用通过调用
accpet()
socket 接口,从 Accept 队列取出连接。
而在 SYN 泛洪攻击下,SYN 队列将不断接收攻击者发送的 SYN 包直到队列溢出,这种情况下,正常用户发送的连接请求也会被延迟或者拒绝。
SYN-Cookie原理
Syn-Flood
攻击成立的关键在于服务器资源是有限的,而服务器收到请求会分配资源 。通常来说,服务器用这些资源保存此次请求的关键信息,包括请求五元组,以及 TCP 选项,如 MSS
、timestamp
、Sack
、Wscale
等等。当后续的 ACK
报文到达,三次握手完成,新的连接创建,这些信息才会被复制到连接结构中,用来指导后续的报文收发。
那么现在的问题就是服务器 如何在不分配资源的情况下
-
验证之后可能到达的
ACK
的有效性,保证这是一次完整的握手 -
保存
SYN
报文中携带的 TCP 选项信息
前两次握手本质上就是双方协商 TCP 状态的过程,有些状态的协商只会出现在前两次握手,比如
MSS
和WSOPT
,且此后都不会再改变。因此,本端必须解析对端发来的 SYN 报文才能知道对方的 TCP 状态。
下面来看 SYN-cookie 是如何在不分配资源的前提下做到以上两点的。
TCP 连接建立时,双方的起始报文序号是可以任意的,既然是任意的,那么为什么不利用它来保存对端的 TCP 信息呢? 这是 SYN cookies
最核心的一点,因此服务器端按照以下规则构造初始序列号:
- 设
t
为一个缓慢增长的时间戳(典型实现是每64s递增一次) - 设
m
为客户端发送的SYN
报文中的MSS
选项值 - 设
s
是连接的四元组信息和t
经过密码学运算后的Hash
值,即s = hash(sip,dip,sport,dport,t)
,s
的结果取低 24 位。
则初始序列号 n
为:
- 高 5 位为
t mod 32
- 接下来3位为
m
的编码值 - 低 24 位为
s
这样就将本次连接的状态信息保存到了 SYN+ACK 报文中,而不是另外在服务器内部开辟资源来保存。
当客户端收到此 SYN+ACK
报文后,它会回复 ACK
报文,且报文中 ack = n + 1
,那么在服务器收到它时,将ack - 1
就可以拿回当初发送的 SYN+ACK
报文中的序号了! 服务器通过这种方式巧妙、间接地保存了一部分客户端 SYN
报文的信息。接下来,服务器需要对 ack - 1
这个序号进行检查:
- 将高 5 位表示的
t
与当前之间比较,看其到达的时间是否能接受。 - 根据
t
和连接元组重新计算s
,看是否和低 24 一致,若不一致,说明这个报文是被伪造的。 - 解码序号中隐藏的
mss
信息
到此,连接就可以顺利建立了。
所以,SYN Cookies 的核心在于:可以在不使用 SYN 半连接队列的情况下成功建立连接 :
如果服务器和客户端 都 打开了时间戳选项,那么服务器可以将客户端在
SYN
报文中携带的 TCP 选项的信息暂时保存在时间戳中。
如何预防 SYN 泛洪
修改 Linux 内核参数
-
增大半连接队列
内容较多,不在此阐述,详细方法请移步本系列另一篇文章-全连接与半连接队列 -
开启 SYN-Cookie
1
echo 1 > /proc/sys/net/ipv4/tcp_syncookies
- 0 值,表示关闭该功能;
- 1 值,表示仅当 SYN 半连接队列放不下时,再启用它;
- 2 值,表示无条件开启功能;
-
减少 SYN+ACK 重传次数
1
2//将重传次数设为1次
echo 1 > /proc/sys/net/ipv4/tcp_synack_retries笔者操作系统上默认为 5 次。