计网上课的时候老师总是说三握四挥,现在回想起来还是历历在目
TCP
TCP概述
每一条TCP连接都有两个端点,这种端点我们叫作套接字(socket),它的定义为端口号拼接到IP地址即构成了套接字,
例如,若IP地址为192.0.0.1 而端口号为8000,那么得到的套接字为192.0.0.1:8000
TCP报文格式
ACK、SYN和FIN这些大写的单词表示标志位,其值要么是1,要么是0;ack、seq小写的单词表示序号
同步SYN:(Synchronize ),SYN=1
表示这是一个连接请求报文,或连接接受报文。SYN
这个标志位只有在TCP建产连接时才会被置1,握手完成后SYN标志位被置0
确认ACK:仅当ACK=1
时,确认号字段才有效。ACK=0时,确认号无效。如:当SYN=1
,ACK=0时表示这是一个连接请求报文段,若同意连接,则在响应报文段中使得SYN=1
,ACK=1
终止FIN:用来释放一个连接。FIN=1表示:此报文段的发送方的数据已经发送完毕,并要求释放
序列号seq:(Sequence Number),占4个字节,表示报文段携带数据的第一个字节的编号,TCP连接中传送的字节流中的每个字节都按顺序编号。例如,一段报文的序号值是 301 ,而携带的数据共有100字段,显然下一个报文段(如果还有的话)的数据序号应该从401开始;,图中的 x 和 y,
确认号ack:占4个字节,期待收到对方下一个报文段的第一个数据字节的序号,例如,B收到了A发送过来的报文段,其序列号seq是1,而数据长度是100字节,这表明B正确的收到了A发送的到序号从1到100为止的数据。因此,B期望收到A的下一个数据序号是100+1,于是B在发送给A的确认报文段中把确认号置为101
下面看一下三次握手
三次握手
(1)第一次握手:Client
将标志位SYN
置为1,随机产生一个值seq=J
,并将该数据包发送给Server,Client
进入SYN_SENT
状态,等待Server
确认。
(2)第二次握手:Server
收到数据包后由标志位SYN=1
知道Client请求建立连接,Server
将标志位SYN
和ACK
都置为1,ack=J+1
,随机产生一个值seq=K
,并将该数据包发送给Client
以确认连接请求,Server
进入SYN_RCVD
状态。
(3)第三次握手:Client
收到确认后,检查ack
是否为J+1
,ACK
是否为1,如果正确则将标志位ACK
置为1,ack=K+1
,并将该数据包发送给Server
,Server
检查ack
是否为K+1,ACK
是否为1,如果正确则连接建立成功,Client
和Server
进入ESTABLISHED
状态,完成三次握手,随后Client
与Server
之间可以开始传输数据了。
四次挥手
由于TCP连接时是全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭,上图描述的即是如此。
(1)第一次挥手:Client
发送一个FIN
,用来关闭C->S的数据传送,Client
进入FIN_WAIT_1
状态。
(2)第二次挥手:Server
收到FIN后,发送一个ACK
给Client
,确认序号为收到序号+1(与SYN相同,一个 FIN占用一个序号),Server
进入CLOSE_WAIT
状态。
(3)第三次挥手:Server
发送一个FIN
,用来关闭S->C的数据传送,Server
进入LAST_ACK
状态。
(4)第四次挥手:Client
收到FIN
后,Client
进入TIME_WAIT
状态,接着发送一个ACK
给Server
,确认序 号 为收到序号+1,Server
进入CLOSED
状态,完成四次挥手。
【问题1】为什么不能用两次握手进行连接?
答:3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。
现在把三次握手改成仅需要两次握手,死锁是可能发生的。作为例子,考虑计算机S和C之间的通信,假定C给S发送一个连接请求分组,S收到了这个分组,并发 送了确认应答分组。按照两次握手的协定,S认为连接已经成功地建立了,可以开始发送数据分组。可是,C在S的应答分组在传输中被丢失的情况下,将不知道S 是否已准备好,不知道S建立什么样的序列号,C甚至怀疑S是否收到自己的连接请求分组。在这种情况下,C认为连接还未建立成功,将忽略S发来的任何数据分 组,只等待连接确认应答分组。而S在发出的分组超时后,重复发送同样的分组。这样就形成了死锁。
【问题2】为什么连接的时候是三次握手,关闭的时候却是四次握手?
答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,“你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。
【问题3】为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。在Client发送出最后的ACK回复,但该ACK可能丢失。Server如果没有收到ACK,将不断重复发送FIN片段。所以Client不能立即关闭,它必须确认Server接收到了该ACK。Client会在发送出ACK之后进入到TIME_WAIT状态。Client会设置一个计时器,等待2MSL的时间。如果在该时间内再次收到FIN,那么Client会重发ACK并再次等待2MSL。所谓的2MSL是两倍的MSL(Maximum Segment Lifetime)。MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。
【问题4】如果已经建立了连接,但是客户端突然出现故障了怎么办?
TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。
【问题5】为什么TCP客户端最后还要发送一次确认呢?
一句话,主要防止已经失效的连接请求报文突然又传送到了服务器,从而产生错误。
如果使用的是两次握手建立连接,假设有这样一种场景,客户端发送了第一个请求连接并且没有丢失,只是因为在网络结点中滞留的时间太长了,由于TCP的客户端迟迟没有收到确认报文,以为服务器没有收到,此时重新向服务器发送这条报文,此后客户端和服务器经过两次握手完成连接,传输数据,然后关闭连接。此时此前滞留的那一次请求连接,网络通畅了到达了服务器,这个报文本该是失效的,但是,两次握手的机制将会让客户端和服务器再次建立连接,这将导致不必要的错误和资源的浪费。
如果采用的是三次握手,就算是那一次失效的报文传送过来了,服务端接受到了那条失效报文并且回复了确认报文,但是客户端不会再次发出确认。由于服务器收不到确认,就知道客户端并没有请求连接。
【问题6】只看到过TCP状态位为 ’FIN +ACK’,但从来没有看过状态位只有 ‘FIN’,怎样解释?
RFC793明确规定,除了第一个握手报文SYN除外,其它所有报文必须将ACK = 1。
【问题7】三次握手过程中可以携带数据吗?
第一次、第二次握手不可以携带数据,而第三次握手是可以携带数据的。
第一次不可以携带数据,也不可以先将数据缓存下来,等握手成功再提交给应用程序,这样会放大SYN FLOOD攻击。如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据,因为攻击者根本就不理服务器的接收、发送能力是否正常,然后疯狂着重复发 SYN 报文的话,这会让服务器花费很多时间、内存空间来接收这些报文。也就是说,第一次握手可以放数据的话,其中一个简单的原因就是会让服务器更加容易受到攻击了。
第三次的话,能够发出第三次握手报文的主机,肯定接收到第二次握手报文,因为伪造IP的主机是不会接收到第二次报文的。所以,能够发出第三次握手报文的,应该是合法的用户。尽管服务器侧的状态还没有“established”,接收到第三次握手的瞬间,状态就会切换为“established”,里面携带的数据按照正常流程走就好
下面有关部分个人喜好网络安全方面
【问题8】SYN洪水攻击原理
利用TCP协议缺陷,发送大量伪造的TCP连接请求,使被攻击方资源耗尽(CPU满负荷或内存不足)的攻击方式
具体原理是:TCP连接的三次握手中,假设一个用户向服务器发送了SYN报文后突然死机或掉线,那么服务器在发出SYN+ACK应答报文后是无法收到客户端的ACK报文的(第三次握手无法完成),这种情况下服务器端一般会重试(再次发送SYN+ACK给客户端)并等待一段时间后丢弃这个未完成的连接。这段时间的长度我们称为SYN Timeout,一般来说这个时间是分钟的数量级(大约为30秒~2分钟);一个用户出现异常导致服务器的一个线程等待1分钟并不是什么很大的问题,但如果有一个恶意的攻击者大量模拟这种情况(伪造IP地址),服务器端将为了维护一个非常大的半连接列表而消耗非常多的资源。即使是简单的保存并遍历也会消耗非常多的CPU时间和内存,何况还要不断对这个列表中的IP进行SYN+ACK的重试。实际上如果服务器的TCP/IP栈不够强大,最后的结果往往是堆栈溢出崩溃—— 即使服务器端的系统足够强大,服务器端也将忙于处理攻击者伪造的TCP连接请求而无暇理睬客户的正常请求(毕竟客户端的正常请求比率非常之小),此时从正常客户的角度看来,服务器失去响应
【问题9】(ISN)seq是固定的吗
三次握手的一个重要功能是客户端和服务端交换ISN(Initial Sequence Number), 以便让对方知道接下来接收数据的时候如何按序列号组装数据。
如果ISN是固定的,攻击者很容易猜出后续的确认号,因此 ISN 是动态生成的。
【问题10】关于SYN-ACK 重传次数的问题
服务器发送完SYN-ACK包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传,如果重传次数超 过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。注意,每次重传等待的时间不一定相同,一般会是指数增长,例如间隔时间为 1s, 2s, 4s, 8s, ….
【问题11】TCP保活机制
若通信双方长时间(7200秒)无数据往来,则会每一隔时间(75秒)给对方发送一个保活探测数据包,要求对方进行回复,若多次(9),保活探测请求没有得到回复,则会认为连接已断开
IP欺骗
IP是识别身份的重要信息,所以它自然也成了黑客们极力想利用的对象。
假设一个用户,已经和服务器建立正常的TCP连接,攻击者会通过构造TCP数据,将自己的IP伪装为与这名用户相同的IP,并向服务器发送一个带有RSI位的TCP数据段。
服务器收到这一数据后,会认为从用户发送的连接有错误,将清空缓冲区中建立好的连接。
这样一来,这名用户再发送数据时,服务器已经没有连接,无法响应,用户必须从新建立连接。
黑客会伪造大量的IP地址,向目标发送RST数据,使服务器无法对正常用户服务,这就是IP欺骗攻击。
TCP重置攻击
TCP连接有一个特别的设定,如果客户端发现到达的报文段,对于相关连接而言是不正确的,TCP会发送一个重置报文段断开连接,防止连接被用来进一步交换信息。
攻击者正是利用了这一机制,通过向通信的一方或者双方发送伪造的重置报文段,让通信方提前关闭TCP连接。虽然服务器仍然可以创建一个新的TCP连接恢复通信,但还是很可能会被攻击者重置。
TCP重置攻击有两种。
一种是攻击者截获了通信双方的交换信息,读取数据包上的序列号和确认应答号后,得出伪装的TCP重置报文段的序列号。
另一种是攻击者无法截取交换信息,无法确定重置报文段的序列号,但通过批量发出不同序列号的重置报文,盲猜序列号,一旦猜中,攻击目的便达成了。