网络基础: 什么是 DDoS ?
2017-10-30 计算机网络
概述
分布式拒绝服务(DDoS)攻击是一种常见的网络攻击形式,攻击者通过向目标服务端发送大量的请求,使目标服务端无法进行网络连接,无法正常提供服务。
DDoS 攻击通常是由大量的分布在全球各地的 “僵尸” 计算机(也称为 “肉鸡” 或 “僵尸网络”)共同发起请求,这些计算机可能被感染了恶意软件,攻击者可以通过远程指令控制来发起攻击,从而达到掩饰攻击来源、加大攻击威力的目的。
1. 协议型攻击
这类攻击类型主要利用网络协议的漏洞或者设计缺陷,消耗目标服务端的资源,其中最著名的莫过于 TCP SYN Flood
攻击,几乎所有大型网站都遭受过这种类型攻击,造成几个小时到几天的服务不可用时间。
全连接与半连接
在描述 TCP SYN Flood 的攻击原理和如何防范之前,先来简单介绍一下 TCP 的全连接和半连接。
全连接是指服务端收到了客户端的 ACK,完成了 TCP 三次握手,然后会把这个连接移动到全连接队列中,全连接队列中的套接字,需要被应用 accept()
系统调用出队取走,服务端才可以开始处理客户端的请求。
也就是说,全连接队列包含了所有完成了三次握手,但还未被应用 accept()
取走的连接,默认情况下,如果全连接队列已满,客户端发送的 ACK 报文会被直接丢掉 (取决于 net.ipv4.tcp_abort_on_overflow 参数设置),返回 connection reset by peer
错误。
半连接是指还没有完成 TCP 三次握手的连接,连接只进行了一半,也就是服务端收到了客户端的 SYN 控制报文之后,服务端会把这个连接放入半连接队列中,然后再向客户端发送 SYN + ACK 控制报文。
和全连接队列一样,半连接队列也有溢出处理机制,默认情况下,如果半连接队列已满,客户端发送的 SYN 报文会被直接丢掉。
SYN Flood (洪泛) 攻击
SYN Flood (洪泛) 攻击就是针对半连接队列的,攻击方不停地向服务端发起连接建立请求 (可以伪造 源 IP 地址
或者直接操作 “僵尸网络”),但是建立连接时只完成第一步,第二步中,当攻击方收到服务端的 SYN + ACK 之后,故意丢掉报文,然后什么也不做。但是服务端会等待客户端的 ACK 包,如果等待时间内未收到,服务端会进行首次重传,然后是第二次重传 …,直到重传次数超过上限制,操作系统会将该连接从半连接队列中删除。
这样重复执行执行攻击过程,如果操作系统删除速度小于半连接队列的增长速度,服务端的半连接队列很快就会被打满,这时其它正常的请求也无法和服务建立连接,攻击者的目的就达到了。
此外,SYN Flood 会导致软中断,CPU 使用率(softirq)飙升。
hping3
hping3
(Redis 作者开发的另外一个工具) 可以构造 TCP/IP 协议数据包,对系统进行安全审计、防火墙测试、DDoS 攻击测试等。
# -S 参数表示设置 TCP 协议的 SYN(同步序列号)
# -p 表示目的端口为 80
# -i u10表示每隔 10 微秒发送一个网络帧
# --rand-source 随机化源 IP 地址 (模拟更加真实的攻击)
# 当前主机: 192.168.0.2, 充当 “客户端” 发起 TCP 连接
$ hping3 -S -p 80 -i u10 192.168.0.30
# 在主机 192.168.0.30 抓包
$ tcpdump -i eth0 -n tcp port 80
# Flags [S] 表示这是一个 SYN 包
# 大量的 SYN 包表明,这是一个 SYN Flood 攻击
09:15:48.287047 IP 192.168.0.2.27095 > 192.168.0.30: Flags [S], seq 1288268370, win 512, length 0
09:15:48.287050 IP 192.168.0.2.27131 > 192.168.0.30: Flags [S], seq 2084255254, win 512, length 0
09:15:48.287052 IP 192.168.0.2.27116 > 192.168.0.30: Flags [S], seq 677393791, win 512, length 0
09:15:48.287055 IP 192.168.0.2.27141 > 192.168.0.30: Flags [S], seq 1276451587, win 512, length 0
09:15:48.287068 IP 192.168.0.2.27154 > 192.168.0.30: Flags [S], seq 1851495339, win 512, length 0
通过抓包输出,可以得出如下结果:
- 客户端 (攻击方) 构造大量的 SYN 包,请求建立 TCP 连接
- 服务端 (被攻击方) 收到 SYN 包后,会向源 IP (客户端) 发送 SYN + ACK 报文,并等待三次握手的最后一次 ACK 报文,直到超时
- 经过一段时间后,服务端的半连接队列会打满,从而无法建立新的 TCP 连接
SYN Flood (洪泛) 攻击防范
如前文所述,SYN Flood (洪泛) 主要攻击的 TCP 的半连接队列,所以 最重要的防范措施就在于: 如何保护半连接队列不被恶意连接打满?
- 服务端的 TCP 连接,会处于 SYN_RECEIVED 状态
- 查看 TCP 半开连接的方法,关键看 SYN_RECEIVED 状态的连接
$ netstat -n -p | grep SYN_RECV
1. 手动处理
手动处理包的核心思路: 先找到状态异常的包,然后手动进行限制或设置直接丢包处理。
首先,我们通过 netstat
命令找出状态为 SYN_RECEIVED
的数据包,然后分析其中异常的数据包。
# -n 表示不解析名字
# -p 表示显示连接所属进程
$ netstat -n -p | grep SYN_RECV
# 发现大量 SYN_RECV 状态的连接,并且源 IP 地址为 192.168.0.2
...
tcp 0 0 192.168.0.30:80 192.168.0.2:12503 SYN_RECV -
tcp 0 0 192.168.0.30:80 192.168.0.2:13502 SYN_RECV -
tcp 0 0 192.168.0.30:80 192.168.0.2:15256 SYN_RECV -
tcp 0 0 192.168.0.30:80 192.168.0.2:18117 SYN_RECV -
...
找出异常数据包的源 IP 地址后,要解决 SYN Flood 攻击的问题,只要丢掉相关的包就可以,可以使用 iptables
命令来完成。
$ iptables -I INPUT -s 192.168.0.2 -p tcp -j REJECT
当然,攻击者不会一个源 IP 地址进行攻击,所以 SYN Flood 攻击中的源 IP 地址并不是固定的,这时手动限制单个 IP 地址的方式可能就不适合了 (当然,也可以将上述命令操作转为自动化脚本)。除此之外,还可以通过限制 SYN 包的速率来实现。
# 限制 SYN 并发数为每秒 1 次
$ iptables -A INPUT -p tcp --syn -m limit --limit 1/s -j ACCEPT
# 限制单个 IP 在 60 秒建立的新连接数为 10
$ iptables -I INPUT -p tcp --dport 80 --syn -m recent --name SYN_FLOOD --update --seconds 60 --hitcount 10 -j REJECT
上述方法虽然可以限制单个攻击源 IP 地址发起的 SYN Flood 攻击,但是如果攻击者发起的是大规模 (僵尸网络) 的 SYN Flood 攻击,该方法可能就无效了。
因为在遭遇大规模的 SYN Flood 攻击时,服务端管理人员很可能无法 SSH 登录(SSH 也是基于 TCP 的)到服务端,更不可能执行上述的相关命令。
2. 增大半连接队列的容量
Linux 查看默认的半连接队列容量:
# 注意: 该数值在不同发行版、Linux 版本中可能不同
$ sysctl net.ipv4.tcp_max_syn_backlog
net.ipv4.tcp_max_syn_backlog = 512
例如将半连接队列容量增大到 65535:
$ sysctl -w net.ipv4.tcp_max_syn_backlog=65535
3. 减少连接 SYN_RECV 状态失败时内核自动重试次数
Linux 查看连接 SYN_RECV 状态失败时内核自动重试次数:
$ sysctl net.ipv4.tcp_synack_retries
net.ipv4.tcp_synack_retries = 5
将自动重试次数减少到 1:
$ sysctl -w net.ipv4.tcp_synack_retries=1
4. 直接拒绝
如果面对海量请求真的处理不过来,直接拒绝就行了。
$ sysctl net.ipv4.tcp_abort_on_overflow
net.ipv4.tcp_abort_on_overflow = 0
启动拒绝连接:
$ sysctl -w net.ipv4.tcp_abort_on_overflow=1
5. TCP SYN Cookies
在默认的 TCP 三次握手过程中,客户端向服务端发送 SYN 数据包,服务端收到之后应该回复一个 SYN + ACK 数据包,并进入 SYN_RECV 状态等待客户端的 ACK 确认,但是,如果攻击者向服务端发送大量的 SYN 数据包而不回复 ACK 确认,服务端的半连接队列就会被打满,无法接受正常的新连接。
TCP SYN Cookies 是一种专门防御 SYN Flood 攻击的方法,SYN Cookies 基于 TCP 连接四元组(包括源地址、源端口、目的地址、目的端口)以及一个加密种子(如系统启动时间),计算出一个哈希值(SHA1),这个哈希值称为 Cookie。
Cookie 就被用作 ISN (Initial Sequence Number),来应答 SYN + ACK 报文,并释放连接状态,当客户端发送完三次握手的最后一次 ACK 后,服务端就会再次计算(反向)这个哈希值,确认是上次返回的 SYN + ACK 的应答报文,然后才会进入 TCP 的连接状态。
开启 TCP SYN Cookies 之后,就不需要维护半连接状态了,自然也就不存在半连接队列的容量限制问题了。
Linux 开启 TCP SYN Cookies:
# 临时配置,重启后配置会丢失
$ sysctl -w net.ipv4.tcp_syncookies=1
net.ipv4.tcp_syncookies = 1
需要注意的是:开启 TCP syncookies 后,内核选项 (半连接容量) net.ipv4.tcp_max_syn_backlog 失效。
如果需要将配置持久化,应该写入 /etc/sysctl.conf 文件:
$ vim /etc/sysctl.conf
# 对应配置修改如下
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_synack_retries = 1
net.ipv4.tcp_max_syn_backlog = 1024
# 配置持久化(动态生效)
$ sysctl -p
2. 反射型攻击
反射型 DDoS 攻击也称为 (流量) 放大型攻击。
攻击者向服务端发送大量的数据包,占用服务端的带宽和资源,造成目标服务端无法正常提供服务,例如 UDP 洪泛、ICMP 洪泛。
UDP 洪泛: 攻击者向目标服务发送大量的 UDP 数据报文试图填充服务端的可用端口,服务端如果没有找到指定端口,会返回 “Destination Unreachable” 应答报文,在海量 UDP 请求下,服务端就会被攻击流量淹没。
ICMP 洪泛:攻击者向目标服务端发送大量的 ICMP ECHO
, 也就是 ping 请求 数据包,目标服务端会耗费大量的 CPU 资源去处理和响应,从而无法响应正常请求。
攻击者也可以利用利用第三方服务或设备,放大攻击流量,典型的如 DNS 攻击。攻击者利用开放的 DNS 服务端向目标系统发送大量的 DNS 查询请求,通过伪造源 IP 地址为被攻击目标,使被攻击目标系统收到大量的 DNS 响应数据包,占用其带宽和服务端资源。
举个小例子来说明这种情况:
+------+ +------+ +------+
| | | | ----> | |
| A | ---> | B | ----> | C |
| | | | ----> | |
+------+ +------+ +------+
攻击者 开放服务端 被攻击者
假设 B 服务端作为开放服务端,监听 UDP 端口 5678,当给这个 UDP 端口发送消息后会做具体的应答。
那么攻击者 A 只需要做以下事情:
- 发送数据给 B,并修改数据报文,伪装自己的源 IP 为 C
- B 接收到 A 的数据后,根据源 IP 将应答发送给 C (因为 A 发过来的报文中,源 IP 是 C)
- C 收到了来自 B 的 “反射性” 攻击,因为 B 将数据数量包放大了,原本 A 可能只发送了 “hello” 给 B,而 B 却发送了 N 个 “hello” 给 C, 如果类似 B 的开放服务端还不止一个,那么理论上,C 受到的攻击可以被无限放大
再举一个 DNS 放大攻击的小例子。
开启 Wireshark 抓包,然后执行下面的命令:
$ dig ANY isc.org
如图所示,发出去的 DNS 请求包数据只有 25 字节.
但是收到的 DNS 应答包数据居然有 3111 字节,足足放大了 124 倍,如果在请求包中伪造一个想要攻击的服务端 IP 地址,该地址就会莫名收到 DNS服务端 3111 字节的回复,利用这个放大攻击,只需要控制少量 (僵尸) 主机,就可以把一个大网站拖垮。
反射型攻击还可以对特定应用程序或服务发送攻击,例如 HTTP 洪泛 (发送大量 HTTP 请求),慢速 HTTP (攻击者向目标服务端发送大量的 HTTP 请求,但是每个请求的发送速度非常缓慢),薄弱环节攻击 (例如网站的关键字搜索功能,攻击者可以自动化伪造不同关键字,发送大量请求,严重影响网站的响应速度,严重时甚至直接使整个网站宕机)。
攻击特点
不论哪种类型的反射型攻击,其攻击方式都具有如下特点:
- 高流量: 攻击者发送大量的数据 (请求报文) 到目标服务端或网络,使其超出正常处理能力范围
- 大规模且难以识别: 攻击者操作数百或数千个攻击节点 (也称为 “肉鸡” 或 “僵尸网络”),形成大规模的攻击,而且攻击节点很难找出共性特征 (攻击流量和正常流量看起来几乎一样)
- 目的明确且无差别攻击: 攻击的目的通常是使目标服务端或网络不可用,所有会攻击目标服务端所有开放端口
- 持续性: 攻击行为可以持续数小时甚至数天
攻击防范
反射型攻击的单纯从技术上很难实现绝对防范,毕竟像 Github 这种级别的网站面对 DDoS 时也只能 “躺平”,目前主流的解决方法基本都是通过 “加钱” 的方式来解决的,例如堆硬件、搭软件、叠带宽等方面。
- 流量过滤和清洗: 使用 (云计算厂商提供的) 专业的 DDoS 防护设备或服务,对流量进行实时监测、过滤和清洗、识别并阻止异常流量
- 入侵检测和防御: 部署入侵检测系统 (IDS) 和入侵防御系统 (IPS),对网络流量进行实时监控和分析、识别并阻止异常流量
- 增加带宽和资源: 增加网络带宽和服务端资源,提高系统抵御大流量攻击的能力
- ISP: 与互联网服务提供商(ISP)合作,共同防范 DDoS 攻击
- CDN: 尽可能使用 CDN 分发流量,最大限度减少攻击对关键业务/服务的影响
- 限流: 关键业务/服务中对网络流量进行限制