首页 资源列表 文章列表

TCP防攻击的完整方法

防 TCP 攻击的完整方法

核心结论:Go 应用层无法防御 SYN Flood,因为攻击包在三次握手阶段就卡死在内核队列,根本到不了你的 net.Listen 。防御必须分层实施。


第一层:内核参数(抗 SYN Flood 的根本)

SYN Flood 原理:攻击者伪造大量源 IP 发送 SYN 包,但不完成第三次握手。这些半开连接占满内核的 SYN 队列,导致正常用户连不上。


必须调整的内核参数:


bash

# /etc/sysctl.conf


# 1. 启用 SYN Cookies(核心防御)

# 队列满时,内核用加密 cookie 代替存储半开连接,无需占用队列

net.ipv4.tcp_syncookies = 1


# 2. 增大 SYN 半连接队列

net.ipv4.tcp_max_syn_backlog = 8192


# 3. 增大已完成连接队列

net.core.somaxconn = 65535


# 4. 减少 SYN-ACK 重试次数(缩短半开连接存活时间)

net.ipv4.tcp_synack_retries = 2


# 5. 复用 TIME-WAIT 连接,加快资源回收

net.ipv4.tcp_tw_reuse = 1

net.ipv4.tcp_fin_timeout = 30

执行 sysctl -p 生效。


关键说明:tcp_syncookies=1 是自动启用的(仅检测到洪泛时激活),正常情况下不增加开销。它让服务器不存储半开连接状态,而是把状态编码进 SYN-ACK 的序列号中,收到 ACK 后再重建,从根本上避免队列耗尽。


第二层:iptables 限速(网络边界拦截)

bash

# 限制单 IP 的 SYN 包速率(每秒最多 5 个,突发 20 个)

iptables -A INPUT -p tcp --syn -m limit --limit 5/s --limit-burst 20 -j ACCEPT

iptables -A INPUT -p tcp --syn -j DROP


# 限制单 IP 并发连接数

iptables -A INPUT -p tcp --dport 8080 -m connlimit --connlimit-above 50 -j DROP

注意:iptables 在协议栈中处理,CPU 开销比 XDP/BPF 高,但足够应对常见攻击。


第三层:Go 应用层(防连接后攻击)

你之前设计的“读 token 才处理”逻辑,防的是完成握手后的攻击,但必须在 Go 代码中补充连接生命周期管理。


1. 给所有连接设置超时(核心)

没有超时,攻击者连上后不发数据,goroutine 永久阻塞。


go

func handleConn(conn net.Conn) {

   defer conn.Close()


   // 设置读写超时(必须!)

   conn.SetReadDeadline(time.Now().Add(10 * time.Second))


   // 读取 token

   token, err := readToken(conn)

   if err != nil {

       return // 超时或读取失败,关闭连接

   }


   // 校验 token

   if !validateToken(token) {

       return

   }


   // 握手成功,取消超时或重置为业务超时

   conn.SetReadDeadline(time.Time{}) // 取消超时


   // 业务循环...

}

关键:每个连接必须有读超时,否则攻击者连上不发数据会耗尽你的 goroutine 和文件描述符。


2. 用 net.ListenConfig 控制底层 socket

go

lc := net.ListenConfig{

   Control: func(network, address string, c syscall.RawConn) error {

       return c.Control(func(fd uintptr) {

           // 启用 TCP Keep-Alive

           syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1)

           // 启用 SO_REUSEPORT(多进程/多协程分散连接压力,需 Go 1.19+)

           syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEPORT, 1)

       })

   },

}

ln, _ := lc.Listen(context.Background(), "tcp", ":8080")

SO_REUSEPORT 让多个监听 socket 绑定同一端口,内核负载均衡分发连接,避免单队列瓶颈。


3. 连接数上限(防耗尽)

go

var (

   currentConns int64

   maxConns     = int64(10000)

)


func handleConn(conn net.Conn) {

   n := atomic.AddInt64(¤tConns, 1)

   defer atomic.AddInt64(¤tConns, -1)


   if n > maxConns {

       conn.Close()

       return

   }

   // ...

}

第四层:反向代理(生产环境必用)

Nginx/HAProxy 做第一道防线:


nginx

# Nginx 限速配置

limit_req_zone $binary_remote_addr zone=conn_limit:10m rate=10r/s;


server {

   listen 8080;

   limit_req zone=conn_limit burst=20 nodelay;

   proxy_pass http://backend;

}

优势:Nginx 有成熟的连接管理、限速、SYN Flood 防护,比裸 TCP 服务安全得多。


第五层:XDP/BPF(极致防护,可选)

XDP 在网卡驱动层丢包,不走协议栈、不建连接,单核可达百万 PPS。


c

// XDP 程序:在网卡层丢弃高频恶意包

SEC("xdp")

int xdp_drop_highfreq(struct xdp_md *ctx) {

   // 查 map,超过阈值的 IP 直接 XDP_DROP

   // 在协议栈之前拦截,零开销

}

适用场景:遭受持续大规模 DDoS 时,XDP 是最后的硬件级防线。


检查清单

层级 措施 是否必须

内核 tcp_syncookies=1 必须

内核 tcp_max_syn_backlog 调大 必须

iptables SYN 限速 + 连接数限制 必须

Go 所有连接设读超时 必须

Go 连接数上限 推荐

反向代理 Nginx/HAProxy 生产环境必须

XDP/BPF 网卡层丢包 大规模攻击时