什么是 SYN 泛洪攻击?

SYN 洪水(半开连接攻击)是一种拒绝服务 (DDoS) 攻击 ,旨在耗尽可用服务器资源,致使服务器无法传输合法流量。通过 TCP 三次握手时,重复发送初始连接请求 (SYN) 数据包,攻击者将占满目标服务器上的所有 半连接队列 ,导致服务器在响应合法流量时表现迟钝乃至全无响应。

SYN 泛洪攻击原理?

  1. 攻击者通常使用伪造的不同的 IP 地址向目标服务器发送大量 SYN 数据包。
  2. 然后,服务器分别对每一项连接请求做出响应(发送 ACK+SYN 包),并确保打开的端口做好接收响应的准备。
  3. 在服务器等待最后一个 ACK 数据包(永远不会到达)的过程中,攻击者将继续发送更多 SYN 数据包,久而久之就会占满服务端各个端口的 半连接队列 ,使得服务器不能为正常用户服务。

半连接队列与全连接队列

正常流程:

  • 当服务端接收到客户端的 SYN 报文时,会将其加入到内核的 SYN 队列(半连接队列)
  • 接着发送 SYN + ACK 给客户端,等待客户端回应 ACK 报文;
  • 服务端接收到 ACK 报文后,从 SYN 队列移除放入到 Accept 队列(全连接队列)
  • 应用通过调用 accpet() socket 接口,从 Accept 队列取出连接。
示意图

而在 SYN 泛洪攻击下,SYN 队列将不断接收攻击者发送的 SYN 包直到队列溢出,这种情况下,正常用户发送的连接请求也会被延迟或者拒绝。

SYN-Cookie原理

Syn-Flood 攻击成立的关键在于服务器资源是有限的,而服务器收到请求会分配资源 。通常来说,服务器用这些资源保存此次请求的关键信息,包括请求五元组,以及 TCP 选项,如 MSStimestampSackWscale 等等。当后续的 ACK 报文到达,三次握手完成,新的连接创建,这些信息才会被复制到连接结构中,用来指导后续的报文收发。

那么现在的问题就是服务器 如何在不分配资源的情况下

  • 验证之后可能到达的 ACK 的有效性,保证这是一次完整的握手

  • 保存 SYN 报文中携带的 TCP 选项信息

前两次握手本质上就是双方协商 TCP 状态的过程,有些状态的协商只会出现在前两次握手,比如 MSSWSOPT ,且此后都不会再改变。因此,本端必须解析对端发来的 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 次。

参考:深入浅出TCP中的SYN-Cookies小林codingcloudflare