TCP三次握手四次挥手以及SYN泛洪攻击

更多文章欢迎关注个人微信公众号:极客熊猫

从TCP 首部结构谈起

本文中我们重点关注TCP首部中的以下字段:

  • source port 和 destination port。
  • Sequence NumberAcknowledgement Number:用以实现可靠数据传输服务。
  • 标志字段中的A、S、F,即ACKSYNFIN

三次握手与四次挥手

图片过大传不上来,欢迎关注我的微信公众号:极客熊猫,查看全文。

TCP的三次握手与四次挥手是老生常谈的问题了,接下来我将结合上图,完整的描述三次握手与四次挥手的过程。

三次握手

三次握手指的是TCP的连接过程。一般而言,连接由客户端发起,服务端被动接受连接。所以我们从客户端发起连接请求讲起。

第一次握手(SYN报文段)

第一次握手由客户端发起,客户端的TCP封装一个特殊的TCP报文段,即SYN报文段

该SYN报文段有以下特点:

  • 其首部的标志字段中的SYN被置为1;
  • 客户端随机选择一个初始序号(client_isn),置于其首部中的Sequence Number字段。

之后,这个特殊的TCP报文段就会被封装为IP数据报,进而封装为以太网帧,然后由物理层送往服务器。

SYN报文段不含应用层数据。

发送SYN报文段后,客户端进入SYN_SENT状态。

第二次握手(SYNACK报文段)

第二次握手由服务端发起,服务端收到客户端发来的SYN报文段后,服务端的TCP也会封装一个特殊的TCP报文段,即SYNACK报文段。

该SYNACK报文段有以下特点:

  • 其首部的标志字段中的SYN被置为1;
  • 其首部中的Acknowledgment Number字段被设为client_isn+1,其中client_isn即客户端发来的SYN报文段中Sequence Number的值;
  • 服务端选择自己的初始序号(server_isn),置于其首部中的Sequence Number字段。

SYNACK报文段也不含应用层数据。

接收SYN报文段并发送SYNACK报文段后,服务端进入SYN_RCVD状态。

若在完成第三次握手之前,服务端就给连接分配资源,将使得服务端易受到SYN泛洪攻击。

初始序号server_isn的选择方法为解决SYN泛洪攻击提供了思路。

第三次握手(ACK报文段)

第三次握手由客户端发起,客户端收到服务端发来的SYNACK报文段后,会再发送一个特殊的TCP报文段,即ACK报文段。

该ACK报文段有以下特点:

  • 其首部的标志字段中的SYN被置为0;
  • 其首部中的Acknowledgment Number字段被设为server_isn+1,其中server_isn即服务端发来的SYNACK报文段中Sequence Number的值;
  • 其首部中的Sequence Number字段被设置为client_isn+1,其中client_isn即之前客户端发送给服务端的SYN报文段中Sequence Number的值。

ACK报文段可以携带应用层数据。

接收SYNACK报文段并发送ACK报文段后,客户端进入ESTABLISHED状态。服务端收到ACK报文段后,也进入ESTABLISHED状态。至此,连接建立成功。

四次挥手

四次挥手指的是TCP连接的断开过程。断开连接可以由客户端发起,也可以由服务端发起,这里我以客户端发起为例进行讲解。

第一次挥手

第一次挥手由主动发起断开的一方发起,在我们这里也就是客户端。客户端的TCP会封装一个特殊的TCP报文段,即FIN报文段。

该FIN报文段有以下特点:

  • 其首部的标志字段中的FIN被置为1。

发送FIN报文段后,客户端进入FIN_WAIT_1状态。

第二次挥手

第二次挥手由服务端发起,实际上是对客户端发来的FIN报文段的回应。服务端收到来自客户端的FIN报文段后,回应客户端一个ACK报文段。

接收FIN报文段并发送ACK报文段后,服务端进入CLOSE_WAIT状态。

客户端收到ACK报文段后,客户端进入FIN_WAIT_2状态。

第三次挥手

第三次挥手仍由服务端发起,本次挥手服务端发送自己的FIN报文段给客户端。

该FIN报文段有以下特点:

  • 其首部的标志字段中的FIN被置为1。

发送FIN报文段后,服务端进入LAST_ACK状态。之后接收到来自客户端的第四次挥手的ACK报文段后,服务端进入CLOSED状态。

第四次挥手

第四次挥手由客户端发起,实际上是对服务端发来的FIN报文段的回应。客户端收到来自服务端的FIN报文段后,回应服务端一个ACK报文段,并进入TIME_WAIT状态。假设ACK报文段丢失,TIME_WAIT状态将使客户端重新发送ACK报文段。

接收FIN报文段并发送ACK报文段后,客户端进入TIME_WAIT状态。等待30s后,客户端进入CLOSED状态。至此,连接彻底断开。

SYN泛洪攻击

SYN泛洪攻击的原理

经过以上讨论可以看到,正常情况下TCP连接建立过程如下:客户端首先向服务端发送SYN报文段,随后服务端回以SYNACK报文段到达客户端,最后客户端向服务端发送ACK报文段完成三次握手,后续就是上层业务数据交互,直到某一方断开连接。

那么假如在握手的过程中,客户端因为莫名崩溃等原因,收到SYNACK报文段后不再回以ACK报文段,服务端将如何处置呢?这时服务端会“优雅地”再等等,会不会是发送的包丢失了呢?于是重新发送一遍SYNACK报文段,再收不到来自客户端的ACK报文段响应的话,就把这次连接丢弃掉。这个过程大约会“优雅地”持续分钟级,这个持续时间被称作SYN timeout时间。

如果只有个别这样的异常情况,目标服务端处理起来自是毫不费力;可如果大量这样的情况出现,对服务端来说就不堪重负了。

如果大量的握手请求涌向TCP服务端,而它们只发出SYN报文段而不以ACK报文段响应结束握手,服务端就要为这每一个请求都维持约一分多钟的连接去等待ACK报文段,也就形成所谓的“半连接”。维护这些半连接是需要消耗很多服务器的网络连接资源的。如果短时间内这些资源几乎都被半连接占满,那么正常的业务请求在这期间就得不到服务,处于等待状态。

更进一步的,如果这些半连接的握手请求是恶意程序发出,并且持续不断,那么就会导致服务端较长时间内丧失服务功能——这就形成了DoS攻击。这种攻击方式就称为SYN泛洪攻击

SYN泛洪攻击的防范措施

SYN cookie

SYN cookie以下列方式工作:

  • 当服务器接收到一个SYN报文段时,它并不知道该SYN报文段是来自一个合法的用户,还是一个SYN泛洪攻击的一部分。因此服务器不会为该连接请求生成一个半开连接。相反,服务端生成一个初始TCP序列号,该序列号是SYN报文段的源和目的IP地址与端口号以及仅有该服务器知道的秘密数的一个哈希函数。这种精心制作的序列号称为cookie。以此cookie代替上文第二次握手中SYNACK报文段中的server_isn。

    重要的是,服务端并不记忆该cookie或任何对应于SYN的其他状态信息。

  • 若客户端是合法的,则它将返回一个ACK报文段。其Acknowledgment Number值应为cookie+1。服务端将使用在SYNACK报文段中的源和目的IP地址与端口号以及秘密数运行相同的哈希函数。若该函数结果加1与cookie值相同,服务端则认为该ACK报文段对应较早的SYN报文段,是合法的,服务端为它生成一个全开的连接。

  • 若客户端没有返回ACK报文段,则初始的SYN报文段并没有对服务端产生危害,因为服务端没有为它分配任何资源。

减短SYN timeout时间

降低SYN timeout时间,使得主机尽快释放半连接的占用,也是防范SYN泛洪攻击的措施之一。

拓展

为什么需要三次握手,两次不行吗?

弄清这个问题,我们需要先弄明白三次握手的目的是什么,能不能只用两次握手来达到同样的目的。

第一次握手:客户端发送网络包,服务端收到了。

这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。

第二次握手:服务端发包,客户端收到了。

这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。

第三次握手:客户端发包,服务端收到了。

这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。

因此,需要三次握手才能确认双方的接收与发送能力是否正常。

试想如果是用两次握手,则会出现下面这种情况:

如客户端发出连接请求,但因连接请求报文丢失而未收到确认,于是客户端再重传一次连接请求。后来收到了确认,建立了连接。数据传输完毕后,就释放了连接,客户端共发出了两个连接请求报文段,其中第一个丢失,第二个到达了服务端,但是第一个丢失的报文段只是在某些网络结点长时间滞留了,延误到连接释放以后的某个时间才到达服务端,此时服务端误认为客户端又发出一次新的连接请求,于是就向客户端发出确认报文段,同意建立连接,不采用三次握手,只要服务端发出确认,就建立新的连接了,此时客户端忽略服务端发来的确认,也不发送数据,则服务端一致等待客户端发送数据,浪费资源。

挥手为什么需要四次?

因为当服务端收到客户端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当服务端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉客户端,“你发的FIN报文我收到了”。只有等到我服务端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四次挥手。

TIME_WAIT状态

TIME_WAIT状态也成为2MSL等待状态。MSL是Maximum Segment Lifetime的英文缩写,可译为“最长报文段寿命”,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。

为了保证客户端发送的最后一个ACK报文段能够到达服务器。因为这个ACK有可能丢失,从而导致处在LAST_ACK状态的服务器收不到对FIN_ACK的确认报文。服务器会超时重传这个FIN_ACK,接着客户端再重传一次确认,重新启动时间等待计时器。最后客户端和服务器都能正常的关闭。假设客户端不等待2MSL,而是在发送完ACK之后直接释放关闭,一旦这个ACK丢失的话,服务器就无法正常的进入关闭连接状态。

什么是半连接队列?

服务器第一次收到客户端的 SYN 之后,就会处于 SYN_RCVD 状态,此时双方还没有完全建立其连接,服务器会把此种状态下请求连接放在一个队列里,我们把这种队列称之为半连接队列。

全部评论

相关推荐

11-27 12:43
已编辑
门头沟学院 C++
点赞 评论 收藏
分享
11-15 17:19
湖南大学 Java
成果成果成果果:这是哪个公司的hr,这么离谱吗,我没见过用性别卡技术岗的,身边女性同学拿大厂offer的比比皆是
点赞 评论 收藏
分享
joe2333:怀念以前大家拿华为当保底的日子
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务