什么是 TCP 协议?
TCP(Transmission Control Protocol)传输控制协议是一种面向连接的、可靠的、基于字节流的传输层通信协议。
- 什么叫做面向连接?
面向连接是指,正式通信前必须要与对方建立连接。事先为所要发送的数据开辟出一条连接好的通道。TCP 连接又包括连接建立和连接终止两个环节,建立连接需要三次握手,而终止连接需要四次握手。
下文会详细讲述什么是三次握手。在这之前,先来研究一下 TCP 协议传输的报文的格式。
报文格式
重要字段讲解:
TCP 序列号(序列码 SN, Sequence Number):四个字节,用来标识从 TCP 源端向目的端发送的字节流,发起方发送数据时对此进行标记。
TCP 应答号( Acknowledgment Number 简称 ACK Number 或简称为 ACK Field ):四字节,只有 ACK 标志位为 1 时,确认序号字段才有效,Ack=Seq+1。
-
标志位共6个:即 URG、ACK、PSH、RST、SYN、FIN 等
(1)URG:该标志位置位表示紧急( The urgent pointer ) 标志有效。
(2)ACK:该标志位取值 1 代表 Acknowledgment Number 字段有效,这是一个确认的 TCP 包,取值 0 则不是确认包。当 TCP 包有效时,称为 ACK 包。
(3)PSH:表示发送端缓存中已经没有待发送的数据,接收端不将该数据进行队列处理,而是尽可能快将数据转由应用处理。
(4)RST:用于复位相应的 TCP 连接。通常在发生异常或者错误的时候会触发复位 TCP 连接。
(5)SYN:起了一个新的连接。该标志仅在三次握手建立 TCP 连接时有效。当这个 SYN 标志位有效的时候称呼这个包为 SYN 包。
(6)FIN:释放一个连接。当 FIN 标志有效的时候称呼这个包为 FIN 包。
三次握手
三次握手过程
三次握手是指建立一个 TCP 连接时,需要客户端和服务端总共发送 3 个包以确认连接的建立。
第一次握手:Client 发送标志位 SYN = 1, 随机产生序列号 seq = J 的数据包到 Server。Server 由 SYN = 1 知道 Client 想建立连接。同时 Client 进入 SYN_SENT 状态。
第二次握手:Server 确认连接,向 Client 发送 标志位 SYN = 1, ACK = 1,确认号 ack number = seq +1 = J+1, 随机产生序列号 seq = k 的数据包。同时 Server 进入 SYN_RCVD 状态。
第三次握手:Client 收到数据包后检查 ack number 是否正确(ack number 是否等于第一次发送的 seq + 1,并且标志位 ACK = 1),若正确,Client 发送标志位 ACK = 1, 并且确认号 ack number = server的seq +1 = K + 1的数据包给 Server。Server 收到后检查 ack number 与 标志位 ACK = 1 则表示连接成功。同时 Client 和 Server 进入 ESTABLISHED 状态,完成三次握手。
为什么是三次握手,不是两次握手?
两次握手的情况,当 Client 发送第一个连接请求由于网络原因长时间滞留,延误到 Client 连接释放以后的某个时间点才到达 Server。该报文本来应该失效了,Server 收到失效的报文,因为是两次握手,Server 发出确认报文后直接建立了连接。这时候 Client 根本没有发出连接请求,也不予理睬 Server 的报文,也不给 Server 发送数据,Server 却以为连接已经建立了,白白的一直在等待 Client 发送数据,耗费资源。
所以,三次握手是建立起可靠的传输信道的最小值。
TCP 协议最常受到的攻击
TCP 协议的设计,会受到哪些攻击呢?
首先理解几个重要概念:未连接队列,SYN-ACK 重传次数和半连接存活时间。
未连接队列:在三次握手协议中,服务器维护一个未连接队列,该队列为每个客户端的 SYN 包开设一个条目,该条目表明服务器已收到SYN包,并向客户发出确认,正在等待客户的确认包。这些条目所标识的连接在服务器处于 SYN_RCVD 状态,当服务器收到客户的确认包时,删除该条目,服务器进入 ESTABLISHED 状态。
SYN-ACK 重传次数 服务器发送完 SYN-ACK 包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传,如果重传次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。注意,每次重传等待的时间不一定相同。
半连接存活时间:是指半连接队列的条目存活的最长时间,也即服务从收到 SYN 包到确认这个报文无效的最长时间,该时间值是所有重传请求包的最长等待时间总和。有时我们也称半连接存活时间为 Timeout 时间、SYN_RCVD 存活时间。
- SYN 攻击原理
由于 TCP 协议的设计,最常见的攻击就是 SYN 攻击。SYN攻击属于DOS攻击的一种,它利用TCP协议缺陷,通过发送大量的半连接请求,耗费CPU和内存资源。大量发送伪造源 IP 的 SYN 包也就是伪造第一次握手数据包,服务器收到 SYN 包后,会发送 SYN-ACK 数据包,由于源 IP 是伪造的,所以服务器不会收到 ACK 数据包,并会不断的进行重发。同时,服务器每接收到一个 SYN 包就会为这个连接信息分配核心内存并放入半连接队列,如果短时间内接收到的 SYN 太多,半连接队列就会溢出,操作系统会把这个连接信息丢弃造成不能连接,当攻击的 SYN 包超过半连接队列的最大值时,正常的客户发送 SYN 数据包请求连接就会被服务器丢弃。目标系统运行缓慢,严重者引起网络堵塞甚至系统瘫痪。
- 如何检测与预防 SYN 攻击
针对 SYN 攻击的几个环节,可以有相应的处理方法:
减少 SYN-ACK 数据包的重发次数(默认是 5 次),避免不断的重发 SYN-ACK 数据包,耗费系统资源。
-
使用 SYN Cookie 技术
- 原理
SYN Cookie是对 TCP 服务器端的三次握手做一些修改,专门用来防范 SYN 攻击的一种手段。它的原理是,服务器接收到 SYN 包并返回 SYN-ACK 包时,不分配一个专门的数据区,而是根据 SYN 包计算一个 cookie 值。这个 cookie 作为 SYN-ACK 包的 Seq Number。当客户端返回 ACK 包时,根据包头信息计算 cookie,与返回的 Ack Number(Seq Number + 1)进行对比,如果相同,则是一个正常的连接,然后分配资源,建立连接。
- Cookie 如何计算
Cookie的计算利用了 Seq Number,长度 32 bit,分为 3 段。如下图:
第一段:5bit 表示时间 t,t 的值是系统时间除以64再对32取余数(time()>>6 mod 32),最高表示到 31。
第二段:3bit 表示 TCP 中最大分段的大小(Maximum segment size),表示最大分段大小的数量只有 8 种,所以服务器在启用了 SYN Cookie 时只能发送八种不同的数值。
第三段:24bit 表示一个由加密散列函数计算得到的值 mac = MAC(A, k),其中 MAC 为带有密钥的散列函数,在 linux 中是 sha1,A = SOURCE_IP || SOURCE_PORT || DST_IP || DST_PORT || t || MSSIND,其中 t 就是上面说的时间,而 MSSIND 是上面说的最大分段大小。k 是服务器提供的密钥。
增加半连接队列(默认是 1024)
限制 SYN 并发数
未完待续……