浏览器中的网络安全问题

1. 多路复用和多路解复用

将传输层报文段中的数据交付到正确的套接字的工作被称为多路分解。在源主机不同套接字中收集报文段、封装头信息生成报文,将报文传递到应用层,这个过程称为多路复用。每个报文段发送出去都要携带IP地址和端口等信息,根据IP地址可以定位到接收报文段的主机,根据端口号将报文段发送给合适的套接字。

1.1 无连接(UDP)的多路解复用

指的是UDP套接字的分配过程。在源端创建UDP的时候需要指定目标端IP地址和端口号,因此一个UDP套接字由一个二元组来标识=>(目标IP, 目标端口)。不同源地址和端口号的UDP报文段到达主机后,如果它们拥有相同的目标地址和目标端口号,那么就会被分配到一个UDP套接字中。每个套接字就是运行在主机上的一个进程。

浅谈传输层 流程图

图中从左到右IP依次为:A B C,下表是IP B 主机的套接字列表:

Socket PID Dest IP Dest Port
77888 P1 B 6428
……

每个在源主机接收的UDP报文段中都提供了(源端口,目标端口)的”返回地址“。

1.2 面向连接(TCP)的多路解复用

指的是TCP套接字的分配过程。一个TCP套接字使用(源IP,源端口,目标IP,目标端口)四元组本地标识,因此一个TCP报文段从网络中到达一台主机的时候,该主机使用这四个值来将报文段定向到合适的套接字。

服务器能够在一个TCP端口上同时支持多个TCP套接字(欢迎套接字,建立连接,根据源IP和源端口分配新的套接字),每个套接字由四元组标识。

浅谈传输层 流程图

上图P4进程下的几个套接字进程标识分别为:P5 P6 P7,下表是IP B 主机的套接字列表:

Socket PID Source IP Source Port Dest IP Dest Port
90000 P4 B 80
90001 P5 A 9157 B 80
90002 P6 C 9157 B 80
90003 P7 C 9775 B 80

IP C主机套接字列表:

Socket PID Source IP Source Port Dest IP Dest Port
80011 P2 B 80 B 5775
90011 P5 A 80 B 9157

一个进程下面可能还有多个线程,这个场景下,根据4元组将报文段内容解复用到不同的线程也是有可能的。

2. 无连接传输:UDP

2.1 UDP协议概述

UDP是一种无连接的、不可靠的传输层协议。它除了复用/分解功能和少量的差错检测之外,几乎没有对IP层增加额外的内容。在RFC文档中有这样关于UDP的描述:

no frills, bare bones internet.

也就是说UDP是尽力而为的服务,报文段可能丢失或者送到应用进程乱序等。无连接是指:UDP发送端和接收端发送报文前不需要握手,每个UDP报文段都是被独立处理的。

UDP适用于对实时性要求较高的场景:

  • 流媒体(丢失不敏感、速率敏感、应用可以控制传输速率)
  • DNS
  • SNMP(简单网络管理协议)
  • QUIC等

2.2 UDP协议特点

  1. 发送报文段之前通信双方不需要握手,相对于TCP来说,没有连接建立的时延,也不需要在端系统中保持状态。
  2. UDP提供尽力而为的服务,不保证数据的可靠交付。
  3. UDP没有拥塞控制和流量控制机制,对数据发送速率没有限制。
  4. UDP套接字只使用一个二元组来标识,支持一对一、多对多、一对多、多对一 的交互通信。
  5. UDP首部较小,只有8个字节。

2.3 UDP报文段结构

浅谈传输层 流程图

UDP校验和是为了检测被传输过程中报文段的差错(比如比特反转)

校验和:源端口+目标端口+长度(越界进位回滚相加再取反);接收方收到UDP报文段后,计算校验和和校验和字段相加是否为FF,不相等即检查到差错,相等也可能有残存差错(可能性极小)

虽然提供了差错校验的机制,但是 UDP 对于差错的恢复无能为力。

3. 面向连接的传输:TCP

3.1 TCP协议特点

TCP是一种面向连接的、可靠的、基于字节流的传输层协议。

  1. TCP协议是面向连接的,通信双方进行通信前,需要三次握手建立连接。端系统中会维护双方连接的状态信息。
  2. TCP协议通过序号、确认号、定时重传、校验和等机制,来提供可靠数据传输服务。
  3. TCP提供的是点对点的服务,即它是在单个接收方和单个发送方之间的连接。
  4. TCP协议提供的是全双工的服务,也就是说连接的双方能够向对方发送和接收数据。
  5. TCP提供了拥塞控制机制,在网络拥塞的时候会控制发送数据的速率,有助于减少数据包的丢失和网络的拥塞程度。
  6. TCP提供了流量控制机制,保证了通信双方的发送和接收速率相同。如果接收方可接收的缓存很小时,发送方会降低发送速率,避免因为缓存堆满而造成数据包的丢失。

3.2 TCP报文段结构

浅谈传输层 流程图

TCP报文段由首部和数据组成,首部一般为20个字节。

源端口和目标端口用于报文段的多路复用/解复用,32比特的序号和确认序号用于实现可靠数据传输服务。

16比特的接收窗口字段用于实现流量控制,表示接收方愿意接收的字节的数量。

4比特的首部长度表示首部占32bit字的数目,最大4*15=60字节。

6比特的标志字段,ACK字段指示确认序号的值是有效的,RST、SYN、FIN字段用于连接的建立和拆除,PSH字段指示接收方应该立即将数据交给上层,URG字段用来指示报文段中存在紧急的数据。

校验和提供了差错校验机制,同UDP。

3.3 TCP连接建立与断开

TCP三次握手

浅谈传输层 流程图

  1. 第一次握手,客户端发送SYN请求连接报文,报文段的首部中SYN标志位位1,序号字段位一个任选的随机数,它代表客户端数据的初始序号。
  2. 第二次握手,服务器接收到客户端发送的SYN连接请求报文后,服务器会先为该连接分配TCP缓存和变量,然后向客户端发送SYNACK报文段,报文段中的SYNACK都为1,代表是一个对SYN连接请求的确认。同时序号字段是服务器端产生的一个任选的随机数,它代表服务器端数据的初始序号。确认号是发客户端发送的序号+1,表示接收到了客户端的报文,给予确认。
  3. 第三次握手,客户端收到服务器的肯定应答之后,也会位这次TCP连接分配缓存和变量,同时向服务器端发哦是那个一个对服务器端的报文段的确认。第三次握手可以在报文段中携带数据。

在我看来,TCP三次握手建立连接的过程就是相互确认初始序号的过程,告诉对方,什么样序号的报文段能被正常地接收。第三次握手的作用就是客户端对服务器端初始序号的确认。

如果只使用两次握手,那么服务器就没办法知道自己的序号是否被确认。同时也是防止失效的请求报文段被服务器接收,而出现错误的情况(比如发起SYN请求超时,客户端重发SYN报文,这时服务器端收到两个连接建立请求)

TCP四次挥手

浅谈传输层 流程图

TCP是全双工的协议,通信双方都可以向对方发送信息,所以断开连接需要经过双方的确认,否则只有一方确认会造成半连接的状态,一方关闭,一方不断超时重传。TCP断开连接的过程如下:

  1. 第一次挥手,客户端认为没有数据发送给服务器了,便发送一个FIN报文,请求断开与服务器的连接。进入FIN_WAIT_1状态。
  2. 第二次挥手,服务器收到了客户端的FIN报文,向客户端发送一个确认报文段,表示已经接收到了客户端释放连接的请求,以后不再接收客户端发送过来的数据。但因为连接是全双工的,服务器此时可能还有数据没有发送完毕,所以此时,服务器端还可以向客户端发送数据。服务器端进入CLOSE_WAIT状态,客户端收到确认后,进入FIN_WAIT_2状态。
  3. 第三次挥手,服务器端发送完全部数据后,向客户端发送FIN报文段,申请断开服务器和客户端的连接,进入LAST_ACK状态。
  4. 第四次挥手,客户端收到FIN请求后,向服务器端发送以恶搞确认回答,进去TIME_WAIT状态,该阶段会持续一段时间(2MSL)。当服务器收到这条报文后,就关闭连接。客户端等待TIME_WAIT等待2MSL没有再收到服务器的报文,也会关闭连接。

最后一次挥手的过程等待2MSL主要是为了防止发送给服务器端的报文没有正常收到,从而导致服务器不能正常关闭。MSL是报文段在网络中的最大生存时间。

3.4 可靠数据传输原理:ARQ协议

ARQ协议指的是自动重传请求,它通过超时和重传保证数据的可靠交付,是TCP实现可靠数据传输的一个很重要的机制。它分为停止等待ARQ协议和连续ARQ协议。

3.4.1 Stop And Wait(停止等待协议)

发送方每发送一个分组,就会为该分组设置一个定时器。规定时间内没有收到已发送分组的肯定回答,则重新发送上一分组。

接受方每接收一个分组,就返回对该分组的肯定回答,当收到冗余的分组的时候,直接丢弃,并返回对上一个到达的分组的确认,表示期望下一个分组。当分组损坏的时候,也直接丢弃。

Stop And Wait协议的主要缺点是信道利用率太低,每次必须等待当前分组被确认才能发送下一个分组。

浅谈传输层 流程图

3.4.2 PipeLine(流水线协议)

流水线协议也叫做连续ARQ协议,为了解决停止等待ARQ协议对于信道利用率过低的问题。它通过连续发送一组分组,然后再等待对分组的确认回答。对于如何处理分组中可能出现的差错恢复情况,一般可以采用GBN协议Slective Repeat协议来实现。

发送窗口

它在发送方维持了一个发送窗口。发送窗口的以前是已经发送并确认的分组,窗口内包含了已发送但未被确认的分组、已确认的乱序分组、和允许发送但未发送的分组,发送窗口以后是缓存中还不允许发送的分组

浅谈传输层 流程图

SND即send,WND即window,UNA 即unacknowledged, 表示未被确认,NXT 即next, 表示下一个发送的位置。

接收窗口

接收端的窗口结构如下:

浅谈传输层 流程图

REV 即 receive,NXT 表示下一个接收的位置,WND 表示接收窗口大小。

(1) Go Back N(滑动窗口协议)

当发送方向接收方发送分组的时候,会依次发送发送窗口中的全部分组,并设置一个定时器。这个定时器可以理解为最早发送但未收到确认的分组(可以看作发送窗口的第一个分组)。如果定时器设定的时间内收到某一个分组的确认回答,则滑动窗口,将窗口的首部移动到确认分组的后一个位置。

如果此时仍然有已发送但未确认的分组,则重新设置定时器,如果没有则关闭定时器。如果定时器超时,则重新发送所有已发送未确认的分组

接收方使用的是累积确认的机制,对于所有按序到达的分组,接收方返回一个分组的肯定回答。如果收到了一个乱序的分组,接收方会直接丢弃,然后返回一个最近按序到达的分组的肯定回答。使用累计确认保证了确认号以前的分组都已经按序到达了,所以发送窗口可以移动到已确认分组的后面。

GBN最大的缺点是使用了累计确认的机制,如果出现了只是滑动窗口的第一个分组丢失,而后面分组都按序到达的情况,那么会造成大量不必要分组的丢弃和重传。

浅谈传输层 流程图

(2) Slective Repeat(选择重传协议)

选择重传协议与滑动窗口协议最大的不同是发送方在发送分组的时候,为每一个分组都创建了一个定时器。当发送方收到该分组的确认回答后,取消定时器,并判断接受分组后,是否存在连续的确认分组,如果有则移动窗口的位置,如果没有,就标记为已经确认的乱序分组,当一个分组定时器到时后,重传这个分组。

在接收方,它会确认每一个正确接收的分组,不管是乱序的还是按序的,乱序的分组会被缓存下来,直到所有的乱序分组到达形成一个有序序列后,再将这一段分组交给上层,接收窗口向后移动。对于不能正确接收的分组,接收方直接忽略。

浅谈传输层 流程图

3.5 TCP的可靠数据传输机制

TCP的可靠数据传输是基于连续ARQ协议和滑动窗口协议的。

TCP协议在发送方维持了一个发送窗口,发送过程同上面的GBN,不再赘述。

不过有一点需要注意,TCP的发送窗口大小是变化的,它是由接收窗口的大小和网络中的拥塞程度来决定的,TCP通过控制发送窗口的长度来控制报文段的发送速率。

如果定时器超时,则重发所有已发送但未确认的分组,并将定时器的时长设置为当前的两倍(说明定时器的时长不太符合当前网络的状况,我想通过快速重传机制重发分组)。那什么是快速重传机制呢?

快速重传指的是:当发送方收到了接收方的3个冗余的ACK确认回答的时候,就重发所有已经发送但没有确认的分组,不必等定时器结束。这是一种指示,表示该报文段以后的报文段可能都已经丢失了。

TCP接收方使用的是累计确认机制,同上面GBN,但是TCP协议不完全和滑动窗口协议相同,因为许多TCP实现会将失序的报文段缓存起来,更像是滑动窗口协议和选择重传协议的混合体

3.6 TCP的流量控制机制

TCP 提供了流量控制的服务,这个服务的主要目的是控制发送方的发送速率,保证接收方来得及接收。因为一旦发送的速率大 于接收方所能接收的速率,就会造成报文段的丢失。接收方主要是通过ACK确认报文中的接收窗口字段来告诉发送方自己所能接收的大小,发送方根据接收方的接收窗口的大小来调整发送窗口的大小,以此来达到控制发送速率的目的。

举个例子:双方三次握手,初始化各自窗口大小,假设均为200字节。发送方发送100字节给接收端。对接收端而言,SND.NXT右移100字节,也就是当前得可用窗口减少100字节。

当这100字节报文段到达接收端之后,会被放入接收端得缓冲队列中。不过由于大量负载的原因,接收端处理不了这么多的字节,只能处理40个字节。剩下的60个留在了缓冲队列。此时接收端回复ACK确认报文的时候就会把接收窗口字段设置为140。

发送端在收到这个报文段后,SND.UNA右移40字节,发送窗口缩小为140字节。

浅谈传输层 流程图

3.7 TCP的拥塞控制机制

流量控制发生在发送端和接收端之间,并没有考虑整个网络环境的影响。如果当前网络环境特别差,特别容易丢包。那么发送端就应该注意一些了,这也是拥塞控制需要处理的问题。

TCP 的拥塞控制主要是根据网络中的拥塞情况来控制发送方数据的发送速率,如果网络处于拥塞的状态,发送方就减小发送的速率,这样一方面是为了避免继续增加网络中的拥塞程度,另一方面也是为了避免网络拥塞可能造成的报文段丢失

对于拥塞控制来说,每条TCP连接都维护两个核心状态:

  • 拥塞窗口(Congestion Window,cwnd)
  • 慢启动阈值(Slow Start Threshold,ssthresh)

设计到的算法有这几个:

  • 慢启动
  • 拥塞避免
  • 快速重传和快速恢复

接下来,我们就来一一拆解这些状态和算法。首先,从拥塞窗口说起。

3.7.1 拥塞窗口

拥塞窗口是指目前自己还能传输的数据量大小。它和接收窗口有什么区别呢?

  • 接收窗口(rwnd)是接收端给的限制
  • 拥塞窗口(cwnd)是发送端给的限制
1
发送窗口大小 = min(rwnd, cwnd)

拥塞控制,就是来控制cwnd的变化。

3.7.2 慢启动

刚开始进行数据传输的时候,由于不知道现在的网络状况是拥塞还是稳定,如果太激进,发包太急可能会造成雪崩式的网络灾难。所以拥塞控制需要一种先以较低的速率发送进行试探的算法来适应整个网络。慢启动的运作过程如下:

  • 三次握手,双方宣告自己的接收窗口大小
  • 双方初始化自己的拥塞窗口(cwnd)大小
  • 发送端每收到一个ACK确认,拥塞窗口+1,也就是说,一个RTT后,cwnd翻倍。

但是这个过程不会一直持续下去,它有一个慢启动阈值,当cwnd达到这个阈值之后,就进入了拥塞避免阶段。

3.7.3 拥塞避免

cwnd到达慢启动阈值之后,一轮RTT由翻倍变为+1,这样窗口的增长速率由指数增长变为加法线性增长。

3.7.4 快速重传和快速恢复

快速重传

指的是,当发送方收到三个冗余的ACK确认(比如5、6、7)时,因为TCP使用的是累计确认的机制,所以很可能已经发生了网络的拥塞而造成报文段丢失。于是立即重传,而不用等定时器到期。

既然要重传,那么只重传第5个包还是5、6、7都重传呢?TCP设计者也不傻,现在的TCP多是GBNSR结合使用的。会记录哪些包到了,哪些没到,针对性重传

在收到发送端的报文后,接收端回复一个ACK报文,那么在这个报文首部的可选项中,可以加上SACK属性,通过left edgeright edge告知发送端已经收到了哪些区间的数据报。这个过程也叫做选择性重传(SACK,Selective Acknowledgment)。

快速恢复

快速恢复是快速重传的后续处理,因为网络中可能已经出现了拥塞。这个阶段,发送端会做如下改变:

  • ssthresh降低为 cwnd的一半
  • cwnd变为拥塞阈值
  • cwnd线性增加

浅谈传输层 流程图

TCP 认为网络拥塞的主要依据是报文段的重传次数,它会根据网络中的拥塞程度,通过调整慢启动的阀值,然后交替使用上面四 种机制来达到拥塞控制的目的。