01. UDP与TCP的区别是什么?
| UDP | TCP | |
|---|---|---|
| 是否连接 | 无连接 | 面向连接 |
| 是否可靠 | 不可靠传输,不使用流量控制和拥塞控制 | 可靠传输,使用流量控制和拥塞控制 |
| 连接对象个数 | 支持一对一,一对多,多对一和多对多交互通信 | 只能是一对一通信 |
| 传输方式 | 面向报文 | 面向字节流 |
| 首部开销 | 首部开销小,仅 8 字节 | 首部最小 20 字节,最大 60 字节 |
| 适用场景 | 适用于实时应用(IP 电话、视频会议、直播等 | 适用于要求可靠传输的应用,例如文件传输 |
02. UDP与TCP的头部
- UDP
UDP 头部包含了以下几个数据:
- 两个十六位的端口号,分别为源端口和目标端口(0~65536)
- 整个数据报文的长度
- 整个数据报文的检验和(IPv4可选字段),该字段用于发现头部信息和数据中的错误
由于在链路层数据帧的最大长度为1500字节,也被称为链路层的MTU(最大传输单元)。而IP头部固定为20字节,所以UDP头部加数据最大为1480字节,出去UDP头部的8个字节外,UDP最大数据携带量为1472字节。
- TCP
UDP 头部主要包含了以下几个数据:
- 两个十六位的端口号
(source port, destination port),分别为源端口和目标端口(0~65536)。 - 16位报文序列号
(Sequence number),用于标识报文段,保证报文段是有序,接收端可以通过序号顺序的拼接报文。 - 16位的同步序列号
(Acknowledgement Number),主要用于数据接收方告诉发送方希望收到的下一个报文序列号。 - 窗口大小
(Window Size),表示还能接收多少字节的数据,用于流量控制。 - 标识符(9个二进制位)
ACK=1:该字段为1表示同步序列号字段有效。此外,TCP还规定在连接建立后传送的所有报文段都必须把ACK置为1。SYN=1:当SYN=1,ACK=0时,表示当前报文段是一个连接请求报文。当SYN=1,ACK=1时,表示当前报文段是一个同意建立连接的应答报文。FIN=1:该字段为1表示此报文段是一个释放连接的请求报文。URG=1:该字段为1表示本数据报的数据部分包含紧急信息,是一个高优先级数据报文。接收端在收到紧急报文后,即使TCP缓冲队列还有未处理的报文,也会优先处理这个紧急报文。比如在下载一个大文件过程中,如果突然取消下载,在取消的操作中,就应该将URG位置为1。PSH=1:该字段为1表示接收端应该立即将数据push给应用层,而不是等到缓冲区满后再提交。一般在发送方发送一个完整的消息被分成多个报文发送时,会将最后一个报文的FLAG标志位中的PSH位置位1,目的是让接收端收到后,尽快处理缓存中的数据发送给应用层,不用等到缓存中数据达到一定量后再发给应用层。RST=1:该字段为一表示当前 TCP 连接出现严重问题,可能需要重新建立 TCP 连接,也可以用于拒绝非法的报文段和拒绝连接请求。
03. 状态机
04. 三次握手
握手目的:
- 同步Sequence序列号(初始序列号ISN)
- 交换TCP通讯参数(入MSS,窗口比例因子,选择性确认,指定校验和算法)
起初,两端都为CLOSED状态。在通信开始前,双方都会创建TCB。 服务器创建完TCB 后便进入LISTEN状态,此时开始等待客户端发送数据。
TCB:Transmission Control Block,传输控制块,保存保存本次连接使用的源端口、目的端口、目的ip、序号、应答序号、对方窗口大小、己方窗口大小、tcp状态、tcp输入/输出队列、应用层输出队列、tcp的重传有关变量等。
第一次握手
客户端向服务端发送连接请求报文段。该报文段中包含自身的初始报文序列号(Sequence number),同时头部标识位的的SYN位被置为1。请求发送后,客户端便进入SYN-SENT状态。
第二次握手
服务端收到连接请求报文段后,如果同意连接,则会发送一个应答,该应答中也会包含自身的初始报文序列号(Sequence number),同时同步序列号里会装在上次发送端的同步序列号(Acknowledgement Number),ACK置为1,表示已收到发送端的连接请求。 发送完成后便进入SYN-RECEIVED状态。
第三次握手
当客户端收到连接同意的应答后,还要向服务端发送一个确认报文。客户端发完这个报文段后便进入ESTABLISHED 状态,服务端收到这个应答后也进入ESTABLISHED状态,此时连接建立成功。
备注:第三次握手中可以包含数据,通过快速打开(TFO)技术就可以实现这一功能。其实只要涉及到握手的协议,都可以使用类似 TFO 的方式,客户端和服务端存储相同的 cookie,下次握手时发出 cookie 达到减少 RTT 的目的。
常考面试题:为什么 TCP 建立连接需要三次握手,明明两次就可以建立起连接
因为这是为了防止出现失效的连接请求报文段被服务端接收的情况,从而产生错误。
可以想象如下场景。客户端发送了一个连接请求 A,但是因为网络原因造成了超时,这时 TCP 会启动超时重传的机制再次发送一个连接请求 B。此时请求顺利到达服务端,服务端应答完就建立了请求,然后接收数据后释放了连接。
假设这时候连接请求 A 在两端关闭后终于抵达了服务端,那么此时服务端会认为客户端又需要建立 TCP 连接,从而应答了该请求并进入 ESTABLISHED 状态。但是客户端其实是 CLOSED 的状态,那么就会导致服务端一直等待,造成资源的浪费。
扩展题:TCP三次握手的优化
- 可以在第三次握手的时候,就把http请求发送过去,这样可以节省1RTT的时间。
fast open技术,在第二次握手的时候,服务端生成一个cookie,与SYN+ACK一并发送给客户端,在下次连接的时候,客户端可以直接跳过三次握手,直接发送一个SYN报文,并且在报文中携带cookie与http请求数据。
扩展题:如何应对SYN攻击
原理:攻击者短时间伪造不同IP地址的SYN报文,快速占满SYN队列队列,使服务器不能为正常用户提供服务。
解决方法:服务端维护两个队列,SYN队列,ACCEPT队列,第一次收到SYN请求时将该连接放入SYN队列,第二次收到SYN+ACK时,将连接从SYN队列移出放入ACCEPT队列。
- 增大SYN队列
- 当SYN队列满后,后面的请求不进入SYN队列,而是直接根据请求生成对应的cookie,与SYN+ACK一起返回,下次正常客户端发报文时,会根据cookie恢复连接。
05. 四次挥手
第一次挥手
在需要关闭连接的时候,客户端会将自己报文中的FIN设置为1发送给服务端,发送完后,客户端的自动进入FIN-WAIT-1状态。
第二次挥手
服务端收到连接释放请求后,会给客户端回复一个ACK,服务端进入CLOSE_WAIT状态,此时表明客户端到 服务端的连接已经释放,不再接收客户端发的数据了。但是因为TCP连接是双向的,所以此时服务端仍旧可以发送数据给客户端。
客户端收到服务端发的ACK后,自动从FIN-WAIT-1状态进入FIN-WAIT-2状态。
第三次挥手
服务端如果此时还有没发完的数据会继续发送,完毕后会向客户端发送一个FIN包,然后便进入LAST-ACK状态。
PS:通过延迟确认的技术(通常有时间限制,否则对方会误认为需要重传),可以将第二次和第三次握手合并,延迟 ACK 包的发送。
第四次挥手
客户端在收到服务端发送的FIN包后,客户端会给服务端回复一个ACK,同时自己进入TIME-WAIT状态。
服务端收到ACK后,直接进入CLOSE,那么服务端这边的连接就彻底关闭了。
客户端在发完ACK后,需要等2个MSL(最大报文段生存期,指报文段在网络中生存的时间,超时会被抛弃)时间,若该时间段内没有服务端的重发请求的话,就进入CLOSED状态。
为什么客户端要进入TIME-WAIT状态,等待2MSL时间后才进入CLOSED状态?
- 确保服务端能进入CLOSED状态
若客户端发完确认应答后直接进入CLOSED状态,如果确认应答因为网络问题一直没有到达,服务端在超时没有收到确认包后,会重发释放连接请求,但由于客户端已关闭,服务端不会收到任何回复,导致服务端永远不能正常关闭。
- 防止已失效的连接请求报文段出现在本连接中
客户端在发送完最后一个ACK确认报文段后,再经过时间2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样就可以使下一个新的连接中不会出现这种旧的连接请求报文段。
06. 重传与确认机制
TCP报文段大小的依据:
- MSS:最大报文段大小,防止在IP层被分段,一般IP层MTU=1500字节,TCP数据长度=1500-20字节IP头部-20字节TCP头部
- 接收方处理能力,比如接收端是台很繁忙的服务器,处理能力有限,那么接收方在协商阶段会给发送方发送一个适合自己的报文段大小。
TCP通过重传与确认机制保证消息可靠送达
发送端A与接收端B建立连接后,A开始给B发送报文,初始报文会生成一个随机序列号,后面的每个报文的序列号为前一个报文序列号+前一个报文长度。
接收端在收到发送端的报文后,会回复ACK确认帧,每个确认帧会设置同步序列号,序列号大小为接收到报文的序列号+1,表示在此之前的报文都收到了。同时,在确认帧中也会携带接收端窗口大小,防止发送端报文发送过快超过接收端处理能力。
如果发送端在一个报文发送后在一个RTO时间内没收到确认帧,就会认为该报文已丢失,会重传该报文。一般RTO重传定时器的时间设置会略大于1RTT。
如何防止TCP序列号回绕(PAWS)
报文中增加Timestamps时间戳,记录每一个报文的发送时间
07. 流量控制
TCP实现了基于滑动窗口的流量控制机制,滑动窗口分为发送窗口与接收窗口。发送窗口(SND-WND)内包括两部分数据:已发送但未收到ACK确认帧的数据与未发送但在接收方处理范围内的数据。发送窗口内去除已发送数据的部分,剩余的就是可用窗口大小。
在发送端开始发送数据之后,接收端将接收到的数据放入缓冲区内,在回复确认帧的时候会在头部带上接收方可用窗口大小的信息,接收方可用窗口大小即接收窗口大小减掉缓冲区内数据大小。发送端在收到接收端发过来的剩余窗口大小时根据该信息及时调整发送窗口大小。
接收方在回复确认帧信息的同时,会不断从缓冲区内取出数据帧进行处理,在处理的同时也会有新的数据帧到达缓冲区,所以接收方的剩余可用窗口总是动态变化的。但是发送方在发送数据的时候,总会根据接收方回复的可用窗口大小,来保证发送的数据不会超过接收方接收窗口大小。
通过这种机制可以进行流量控制,保证不会因为数据发送过来超过接收方处理能力。
08. 拥塞控制之CUBIC算法
由于TCP协议向应用层提供不定长的字节流发送方法,使得TCP协议先天性的有意愿去占满网络中的带宽,但是当网络中的许多TCP连接同时试图去占满网络带宽的时候,就有可能发生恶性拥塞事件。 TCP拥塞控制算法有利于降低网络中的拥塞,提升所有TCP连接的发送速度。
拥塞控制算法主要分为两大类,一是以丢包为依据的控制算法,比如慢启动、拥塞避免、快速重传、快速恢复,另外一种是以探测带宽为依据的控制算法,比如谷歌16年提出的BBR算法。
慢启动
发送窗口的大小取决于接收端剩余窗口与拥塞窗口两者中较小的那个。在TCP连接建立之后,发送端的拥塞窗口会初始化一个较小的值。比如1个数据包的大小,所以在发送初期,发送端窗口大小主要取决于拥塞窗口大小。在发送数据包的过程中,每收到1个确认帧,拥塞窗口的大小扩大1倍,以此按指数级增大。
慢启动解决的问题是,在数据开始传输时,发送方并不清楚网络状态是否繁忙,所以刚开始的时候先以较慢速度发送,当发送方确认报文没有发生丢失的时候,再快速增加拥塞窗口大小。
以前一般拥塞窗口初始化为1SMSS,后来修改为2-4个,现在一般初始为10个SMSS。
拥塞避免
慢启动以指数级增加拥塞窗口,所以一旦开始出现丢包,丢包数量会很大,拥塞避免算法可以避免这个问题。拥塞避免通过定义一个慢启动阈值的变量,当拥塞窗口大小达到慢启动阈值的时候,就不再以指数级方式增长,而是改为以线性级方式增长。
在此过程中如果发生丢包,就会将慢启动阈值重新设置为拥塞窗口的一半,然后拥塞窗口降为一个比较小的数字,再次启动慢启动算法,达到相应阈值后,再启动拥塞避免算法。
快速重传
当出现丢包时,我们会重新执行慢启动操作,此时意味着拥塞窗口大幅度下降,我们的发送速率也会大幅度下降。当丢包出现的场景并不是很严重的时候,我们也可以采用快速重传与快速恢复。
当丢包发生时,接收方没收到丢失的数据包,后续收到的数据包在序列号上与已收到的不能保持连续,我们后续的这些数据包为失序报文段。
快速重传算法对于接收方来说:
- 当接收到一个失序报文段时,立刻发送他所期待的缺口ACK序列号。
- 当接收到填充失序缺口的数据段时,立刻发送他所期待的下一个ACK序列号。
比如当接收方依次收到报文1 2 3 4 6,当收到报文6时,发现此时没收到报文5,报文6作为失序报文,此时接收端会立刻回复一个带有报文5序列号的ack回复报文,表示希望收到报文5,在没有收到报文5之前,接收方每收到一个失序报文段,比如6 7 8,都会回复一个带有报文5序列号的ack回复报文,直到收到报文5后,接收端会回复他所期待的下一个报文的ack序列号。假设此时已经收到了6 7 8,那此时接收方会回复9号报文的序列号。
快速重传算法对于发送方来说:
- 当收到3个重复的失序ACK报文段(4个相同的失序ACK段)时,不再等待重传定时器的触发,立刻基于快速重传机制重发报文段。
快速恢复
快速重传意味着出现了丢包,但丢包不一定意味着需要启动慢启动。由于慢启动会突然减少数据流,但是快速重传场景下,网络还是通的,我们还在频繁接收到ACK报文段。
快速恢复执行的时机在发送方启动快速重传之后,在正常未失序ACK段到达前,具体做法为:
- 将慢启动阈值
(ssthresh)设置为当前拥塞窗口(cwnd)的一半,将当前拥塞窗口设置为慢启动阈值+3MSS - 每收到一个重复ACK,拥塞窗口增加1个MSS
- 当新数据ACK到达后,设置拥塞窗口等于慢启动阈值
介绍下TCP选择性重传算法(SACK)
TCP发送端在发送1 2 3 4 5 6 7 8号报文,假设其中的5号报文丢失了,那么发送端是只重发5号报文还是5号后面的所有报文都需要重发呢?
在TCP的选择性重传算法中,如果回复的ACK报文中的Option字段的存在类型为4的字段,表示支持SACK选择性确认中间报文段功能,此时Option字段的存在类型为5的字段,里面记录了确认窗口中间的报文段,也就是丢失报文段后面继续收到的报文段。
比如接收方一次收到了1 2 3 4 6 7 8号报文,5号未收到,那么接收方在收到6号报文的时候,在回去的ACK确认报文中,还是会回复报文5的序列号,表明希望收到5号报文,但同时会在option配置中写入一个类型为5的字段,里面带上6号报文的序号,具体记录方式为left edge就是左边界,记录报文6的序列号,还有一个right edge,值为报文6序列号加上报文6的长度的值。
如果回复确认报文后继续受到7号报文,依旧没有收到5号报文,那么此时的回复报文里option里类型为6的字段里,left edge依然是6号报文的序号,但right edge的值变为6号报文的序号加上7号报文与7号报文的总长度的值。
09. 拥塞控制之BBR算法
CUBIC算法是基于丢包的拥塞控制算法,只有在已经发生丢包后,才会启动该算法,2016年谷歌提出的基于带宽测量的拥塞控制算法BBR,BBR算法提出后,对拥塞控制有巨大的性能提升。在Linux4.9内核引入该算法,windows10默认使用BBK算法。
CUBIC算法在数据发送的过程中,速率不断增大,当网络中的某个路由器的发送速率慢于报文的接收速率,会导致路由器中的报文队列延长。此时即使没有出现丢包现象,但由于路由器中排队也会浪费一部分时间,导致报文的RTT变长。当报文队列占满后路由器发生丢包现象,此时会导致快速重传,发送速率会突然降低大概一半,网络带宽在这段时间又没有被占满。
- 报文在路由器中排队导致RTT偏大
- 不能很好地利用带宽
- 基于丢包的拥塞控制点
- 高时延,大量丢包
- 随着内存便宜,网络中的路由器倾向于分配更长的缓存队列,导致时延更高
- 最佳控制点
- 最大带宽下
- 最小时延
- 最低丢包率
以下是各个算法对带宽的利用对比图,可以发现BBK对带宽利用率最高
基于丢包的拥塞控制算法会不断增加发送速度,直到发生丢包,在丢包发生的时候,网络中的路由器等设备已经产生了大量的排队情况,此时数据包的RTT是偏大的,丢包发生后立刻大幅度减少发送速度,又会导致带宽利用率下降。
BBR算法试图为发送端找到一个最佳发送速率,在这个发送速率下,网络设备内不会产生数据包队列积压,收到数据包后可以立马转发出去。这时候的数据包拥有最小的RTT并且整体带宽占用也最大。
BBR算法试图找到一个最佳测量带宽,那么又是如何测量带宽的呢?先测出RTprop,即消息传输的时延,再由传输时延计算出最大网络传输带宽BtlBw。
我们在计算RTT的时候,即消息一次往返时间,这个时间除了消息在链路中传输的时间外,还包括消息在各个网络设备中排队的时间,还有ack延迟确认的时间。而BBR算法希望得出一个仅仅链路传输的时间,对于确定的链路,这个时间应该是固定的。如果我们把除正常链路传输外耽误的时间称为网络噪声的话,那么实际上:
RTT = RTprop + 网络噪声
BBR通过统计每次的RTT值,取其中最小的RTT,假设此次的网络噪声近似为0,那此时的RTprop就等于当前统计到的最小的RTT。而BtlBw是通过多次测量取最大发送速率,就等于BtlBw。
当TCP链路发生变化后,之前测出的RTprop和BtlBw就失效了,此时发送端需要想办法感知,然后重新计算RTprop和BtlBw。BBR算法通过定期提高与降低发送速率,在这个过程中,如果链路发生了变化,我们就可以得到新的RTprop和BtlBw。具体为在每一个固定周期比如400ms的时间内,BBR算法将发送速率变到原来的1.25倍,然后又将变为0.75倍,后面以1倍的速度进行发送。
10. TLS1.2握手过程是怎样的
TLS握手要保证安全,必须满足消息传输的4个条件,即机密性、完整性、身份认证、不可否认。TLS通过对称加密与非对称加密组合模式保证消息的机密性,通过数字签名实现身份认证与不可否认。
对称加密
AES:密钥长度可以是128、192 或 256。它是 DES 算法的替代者,安全强度很高,性能也很好。ChaCha20:Google设计的另一种加密算法,密钥长度固定为256位。
加密分组模式
对称加密只能加密固定长度消息,想要实现加密任意长度的消息,需要使用到加密分组模式,常用的分组模式有
GCMCCMPoly1305
把对称加密与加密分组模式组合起来,可以得到TLS密码套件中定义的对称加密算法。比如
AES128-GCM:使用密钥长度为128位的AES算法,使用的分组模式是GCMChaCha20-Poly1305:使用ChaCha20算法,使用的分组模式是Poly1305
非对称加密
RSA:安全性基于“整数分解”的数学难题,秘钥长度为1024或者2048位。没有前向保密性。ECC:它基于“椭圆曲线,子算法ECDHE用于密钥交换,有前向保密性,ECDSA用于数字签名。 离散对数”的数学难题
TLS1.2握手过程
TLS握手主要分为三个阶段
第一阶段:客户端获取服务端公钥并与服务端共享各自的随机数
- 客户端将自己生成的随机数
client_random,TLS版本号,加密套件列表发送到服务端 - 服务端收到消息后,将自己生成随机数
server_random,确定的TLS版本号、加密套件,还有服务端证书,利用散列函数计算证书的信息摘要后用私钥加密的结果,连同服务端秘钥交换协议的公钥,一并发给客户端。
第二阶段:证书验证
- 客户端接收到服务端发来的证书后,先读取证书中的相关的明文信息,采用相同的散列函数计算得到信息摘要,然后利用对应
CA的公钥解密签名数据,对比证书的信息摘要,如果一致,则可以确认证书的合法性;然后去查询证书的吊销情况等。如果有效就会将自己的秘钥交换算法的公钥发送给服务端。
第三阶段:主秘钥生成
-
客户端、服务端共同用双方的公钥通过秘钥交换算法算出预主秘钥
pre-master,然后用client_random、server_random和预主秘钥pre-master利用伪随机数算法算出主秘钥Master Secret。 -
秘钥生成后,客户端会把之前给服务端发送的数据做个摘要,用服务端的公钥加密后发送给服务端,然后再发一个
Finished的消息, 告诉服务端做个验证。 -
服务端也同样将之前发送的数据做个摘要,并用客户端的公钥加密后发给服务端,然后再发个
Finished消息。 ,让客户端也做个验证。
传统RSA秘钥交换算法与ECDHE的区别
ECDHE的安全性更高,主要是在ECDHE算法可以满足在客户端与服务端各各自利用前期交换的参数生成相同的预主秘钥pre-master,并且用这个预主秘钥pre-master进一步生成会话秘钥Master Secret,这个过程不需要通过网络传递,保证了秘钥的安全性。而RSA是在握手的过程中由客户端生成预主秘钥pre-master需要通过网络发送给服务端,两端再利用各自取的的参数生成会话秘钥Master Secret。ECDHE的安全性更高,还因为其具有前向安全性,假设有这么一个很有耐心的黑客,一直在长期收集混合加密系统收发的所有报文。如果加密系统使用服务器证书里的RSA做密钥交换,一旦私钥泄露或被破解(使用社会工程学或者巨型计算机),那么黑客就能够使用私钥解密出之前所有报文的pre-master,再算出会话密钥,破解所有密文。这就是所谓的“今日截获,明日破解”。而ECDHE算法在每次握手时都会生成一对临时的公钥和私钥,每次通信的密钥对都是不同的,也就是“一次一密”,即使黑客花大力气破解了这一次的会话密钥,也只是这次通信被攻击,之前的历史消息不会受到影响,仍然是安全的。
双向认证
单向认证握手过程,只认证了服务器的身份,而没有认证客户端的身份。这是因为通常单向认证通过后已经建立了安全通信,用账号、密码等简单的手段就能够确认用户的真实身份,但为了防止账号、密码被盗,有的时候(比如网上银行)还会使用 U 盾给用户颁发客户端证书,实现双向认证,这样会更加安全。
双向认证的流程也没有太多变化,只是客户端在给服务端发送公钥的同时会把自己的证书信息发送到服务端,服务端收到后也把证书链走一遍,验证客户端的身份。
TLS1.3优化
强化安全
在 TLS1.3 中废除了非常多的加密算法,最后只剩如下几种
- 对称加密算法:
AES、ChaCha20 - 分组模式:
AEAD的GCM、CCM和Poly1305 - 摘要算法:
SHA256、SHA384 - 密钥交换算法只有
ECDHE和DHE
最后只剩下5个套件
TLS_AES_128_GCM_SHA256TLS_AES_256_GCM_SHA384TLS_CHACHA20_POLY1305_SHA256TLS_AES_128_GCM_SHA256TLS_AES_128_GCM_8_SHA256
提升性能
握手改进
大体的方式和TLS1.2差不多,不过和TLS1.2相比少了一个RTT,服务端不必等待对方验证证书之后才拿到client_params,而是直接在第一次握手的时候就能够拿到,拿到之后立即计算会话秘钥,节省了之前不必要的等待时间。同时,这也意味着在第一次握手的时候客户端需要传送更多的信息,一口气给传完。
会话复用
会话复用有两种方式: Session ID和Session Ticket。
先说说最早出现的Seesion ID,具体做法是客户端和服务器首次连接后各自保存会话的ID,并存储会话密钥,当再次连接时,客户端发送ID过来,服务器查找这个 ID 是否存在,如果找到了就直接复用之前的会话状态,会话密钥不用重新生成,直接用原来的那份。
但这种方式也存在一个弊端,就是当客户端数量庞大的时候,对服务端的存储压力非常大。
因而出现了第二种方式——Session Ticket。它的思路就是:服务端的压力大,那就把压力分摊给客户端呗。具体来说,双方连接成功后,服务器加密会话信息,用Session Ticket消息发给客户端,让客户端保存下来。下次重连的时候,就把这个 Ticket 进行解密,验证它过没过期,如果没过期那就直接恢复之前的会话状态。
这种方式虽然减小了服务端的存储压力,但与带来了安全问题,即每次用一个固定的密钥来解密 Ticket 数据,一旦黑客拿到这个密钥,之前所有的历史记录也被破解了。因此为了尽量避免这样的问题,密钥需要定期进行更换。 总的来说,这些会话复用的技术在保证1-RTT的同时,也节省了生成会话密钥这些算法所消耗的时间,是一笔可观的性能提升。
PSK
刚刚说的都是1-RTT情况下的优化,那能不能优化到0-RTT呢?答案是可以的。做法其实也很简单,就是在第一次TCP握手的时候就携带相关数据,TLS握手没有占用任何RTT,这种方式被称为Pre-Shared Key,即PSK。
无论是Session ID、Session Ticket还是ORTT都面临着同样一种问题,就是重放攻击,所以Session ID、Session Ticket、ORTT都需要设置合理的过期时间。