“三次握手”这道经典面试题答案是该更新了

从 SSLv2.0 到 TLSv1.3

Posted by 薛以致用 on June 13, 2020

面试过程中,一道经典的问题就是 TCP 三次握手和 SSL/TLS 握手协议过程,这道题巧妙的地方在于,可以考察最基础的网络通信知识,还可以要求候选人白板画出具体的交互图,并且可以再次扩展到网络通信性能优化和安全相关领域知识。今天我们的常见的 HTTP 协议从 1.1 已经进化到 QUIC 和 HTTP/3,而加密通信也从 SSLv2(加密套接字协议层)发展到 TLSv1.3,未来已来,今天我们一起来“旧知新解”。

TCP 连接为啥要“三次握手建立”和“四次握手断开”?

引用网友有一段通俗的解释,三次握手是不可靠的通信双方为了就某一个问题(连接或协商密钥)达成一致所需要的最小数量的沟通次数。

而 TCP/IP 网络通信的基础是套接字(Socket)接口,它抽象出通信所需要的 5元组(协议,本机IP地址,本地协议端口,远程主机IP地址,远程主机协议端口);Socket 支持传输层 TCP 和 UDP 协议,当使用 TCP 协议是,我们就把该 Socket 连接成为 TCP 连接;通常的 HTTP 请求都是基于 TCP 协议进行通信。

TCP 协议的头部,跟三次握手相关的有如下字段:

  • Sequence Number(图上缩写为 seq): 用来标注数据流序号(发送端)
  • Acknowledgment Number(图上缩写为 ack): 确认序列号包含发送确认的一端所期望收到的下一个序号,应当是上次已成功收到数据字节序号加 1;
  • TCP Flag(标志位)
    • ACK :响应标志,建立连接后,所有的有效报文都必须为 1;
    • SYN :同步序号,用来建立连接;
    • FIN :表示数据传输完成;

不同的 SYN 和 ACK 组合,在建立连接时,表示不同含义:(SYN=1,ACK=0)表示请求连接,(SYN=1,ACK=1)响应同意连接请求;

通过图示,我们一起来理解下经典的 TCP 建立连接的三次握手和断开时的 四次握手:

tcp握手

建立连接如果不采取三次握手,而是两次,就 A 发起, B确认响应就建立连接,会出现什么问题呢?比如 A 本意只想建一个连接,但发送第一个建立连接请求 R1 后,迟迟没得到响应,就又发送了一次建立连接请求 R2, R2 请求顺利得到响应并建立连接,但 R1 后来也到达 B端,B端还以为 A 又想建立一个新请求,所以,就响应并一直等待 A 发送数据(此时 B 是有资源消耗的),但 A 早就丢弃了 R1, 那 B 端资源就在浪费空跑,所以引入第三次握手 A 再次确认,避免 B 会错意导致资源浪费。

四次握手断开连接,放在 TCP 全双工的上下文就比较好理解,A 和 B 都可以主动发送数据给到对方(全双工),A 主动断开,一个请求,一个确认,但也要等待 B 确认它的数据已经发送完毕,因此,也需要两次握手来确认断开连接。

HTTP vs HTTPS

HTTP 协议

超文本传输协议(HTTP)存在非常大的安全隐患,由于明文方式发送信息,易遭受窃听、篡改、劫持等攻击,而 HTTPS 则在传输层(TCP)和应用层(HTTP)之间加入了一个 SSL/TLS 加密协议,通过证书来验证身份,并加密数据传输,保障数据通信过程中的保密性(第三方无法窃听)、完整性(防止篡改)和身份认证(防止冒充)。

SSL/TLS 发展历史

SSL(Secure Sockets Layers)安全套接层/TLS(Transport Layer Security)安全传输层协议都是一种加密协议,满足网络上服务器之间、应用和服务器之间应用之间认证和数据加密需求,如下图示发展历史所示,目前只建议使用 TLSv1.2和 TLSv1.3,其他版本都有安全问题或过渡版本,不建议启用,参考 IETF(Internet Engineering Task Force)说明;

SSL/TLS history

那 TLS 和 SSL 有什么区别吗?

“The differences between this protocol and SSL 3.0 are not dramatic, but they are significant enough that TLS 1.0 and SSL 3.0 do not interoperate.”

SSL/TLS 协议的目包含验证,防篡改,加密,验证支持客户端和服务器端验证;防篡改主要利用哈希算法,保障完整性;加密,体现两个方面,一个是握手过程中的非对称加密建立信道,另外一个信道建立之后的块数据对称加密。

证书不等同于协议

务必注意,证书独立于协议,在升级 SSL 为 TLSv1.3 的过程中, 没有必要纠结”SSL 证书”还是“TLS 证书”,在 TLS 没被广泛认知的情况下,“SSL/TLS证书”是个临时的妥协方案,协议是 Web 服务器的配置决定的,而不是证书本身。

在每一次安全连接开始,会有一个 TLS 协议握手过程(后面会具体展开),该过程客户端和服务器端会协商一个都支持的 “加密套件(Cipher Suite)”,加密套件是一组算法,它们协同工作,证书在握手协商加密套件过程中发挥作用。

经典的加密套件(TLSv1.0~1.2)包含如下三大算法,以 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 为例:

  • 密钥交换 (Key Exchange),例如 ECDHE_RSA,非对称加密算法,用于决定客户端与服务器之间在握手时如何身份验证
  • 消息身份认证 (Message Authentication),例如 SHA384,用于创建消息摘要,消息流每个数据块的加密散列,用于验证消息完整性
  • 批量加密算法(Bulk Encryption)例如 AES_256_GCM,对称加密算法,用于加密消息流。它还包括密钥大小及显式和隐式初始化向量(密码学随机数)的长度

加密套件

TLS 握手协议

TLS 协议由三块组成,TLS 记录协议(TLS Record)基于可靠传输层 TCP 之上,与具体应用无关、 TLS 握手协议(Alert);为了更好的了解握手协议,我们需要参考 TLSv1.2 的完整定义RFC5246 和 TLSv1.3 正式版 RFC8446

Handshake

在 TLSv1.2 协议中,握手协议需要2个往返请求(2-RTT)才能协商好生成一个“对话密钥”,具体握手过程如下描述:

  • 客户端发出请求(Client Hello 报文),包含信息如下 (TLS 版本信息,客户端生成的随机数1,支持的加密套件列表,支持的压缩算法列表,扩展字段)
  • 服务器端回应(Server Hello)
    • Server Hello 报文,包含服务器支持的(确定 TLS 版本,服务器生成的随机数2,确定一种加密套件,确定一种压缩算法,扩展字段)
    • 服务器端证书消息,按照信任链发送完整的服务器端证书列表,通常建议优化到 3KB 以内的证书数据传输在如何优化数据传出大小(Data Transfer Out)场景下,这块的消耗优化值得关注。
    • (可选)服务器密钥交换(Server Key Exchange),仅当服务器提供的证书不足以允许客户端交换预主密钥时,才会发送此消息;对于 ECDHE_RSA 或 DHE 密钥交换算法来说这个过程是必须的,会生成一对公钥和私钥,公钥发给客户端,私钥保留,用于生成服务器端预主密钥;
    • (可选)可选验证客户端身份(发送客户端证书请求,验证客户端身份)
    • Server Hello Done 报文
  • 客户端回应(Certificate Verify)
    • (可选)Client Key Exchange:如果是ECDHE_RSA 或 DHE 密钥交换算法,那么客户端也类似会生成一对DH(Diffie-Hellman)密钥对,并通过该消息与服务器共享公钥信息。
    • Certificate Verify:验证服务器证书合法性,如果有警告,由用户选择是否继续访问;证书如果没有问题,客户端就会取出服务器证书中的公钥;发送如下信息给到服务器:(1)一个随机数(又被称为预主密钥),用服务器公钥加密,(2)Change Cipher Spec,编码改变通知表明后续用双方协商的加密方法
    • 生成测试数据
    • 客户端握手结束通知
  • 服务器端:
    • New Session Ticket,服务器端告知客户端将生成新的 Session Ticket用于会话保持
    • 生成测试数据
    • Change Cipher Spec:同样服务端也要发送这段信息,作用与客户端一致

握手完成之后,加密通道开始建立并交换数据,握手过程中,不管利用什么密钥交换算法,最终客户端和服务器端都分别计算出相同的“预主密钥 (Pre Master Secret)”,那双方后面交互的数据就是通过相同的“预主密钥”来加解密的吗?上面的握手过程,Hello 问候消息交换的两个随机变量还没有使用:客户端生成的随机数1和服务器生成的随机数2,为了保证数据的安全性和完整性,TLS 协议从预主密钥首先计算出主密钥(Master Secret),主密钥通过握手期间交换的服务器和客户端生成的随机数的值进行密钥扩展,而记录协议需要基于主密钥扩展字符,生成如下密钥类型用来加密数据和保证数据完整性:

  • 会话密钥 (Session Secret 或者 Session Key),即 Write MAC key,客户端和服务器端使用不同的会话密钥来发送数据
    • 客户端写入 MAC 密钥(Client Write MAC Key): 客户端用来创建 MAC,服务器用来验证 MAC
    • 服务器写入 MAC 密钥(Server Write MAC Key):服务器用来创建 MAC,客户端用来验证 MAC
  • 写入加密密钥(Write Encryption Key):
    • 客户端写入加密密钥(Client write encryption key):客户端用赖加密数据,服务器用来解密数据
    • 服务器写入加密密钥(Server write encryption key):服务器用来加密数据,客户端用来解密数据

其中 消息验证代码 MAC(Message Authentication Code) 是一个数字签名(HASH 值),用来验证数据完整性,防篡改,发送之前将 MAC 值包含在加密数据中,会话密钥用于从数据中生成 MAC,避免传输过程中攻击者无法从数据中生成同样的 MAC 值。TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 加密套件中,尾部的 SHA384就是协商一致的生成 MAC 值的散列算法。

TLSv1.3 的更快、更安全改进

TLSv1.3 协议文本的 1.2 章节,总结了 TLSv1.3 的主要变化,主要是性能更快和更安全,与 TLSv1.2 完全不兼容是一个大幅度改造的安全通信协议:

  • 更快:
    • 建立连接握手协议过程从 2-RTT 减少一半到 1-RTT,即握手时间减半
    • 支持 “不额外增加网络延时”模式(0-RTT),对于近期访问过的站点,可以直接发送加密数据而不需要握手,即恢复会话只需要0-RTT
  • 更安全:
    • 修枝剪叶,比如不支持前向安全的 RSA 密钥交换算法,易受 BEAST 和 Lucky 13 攻击 CBC 模式密码,废弃了 SHA1、MD5 等哈希算法等等。
    • 相比过去的的版本,引入了新的密钥协商机制 — PSK(PreSharedKey)
    • ServerHello 之后的所有握手消息采取了加密操作,可见明文大大减少,连证书信息也是加密的(TLSv1.2需要在双方明文交换了 Key exchange 信息之后才会走加密通道)
    • 不再允许对加密报文进行压缩、不再允许双方发起重协商

相对于 TLSv1.2 的握手过程,TLSv1.3 去掉了 ChangeCipherSpec 报文消息,而在 TLSv1.2 协议中,TLS False Start特性 RFC7981 在客户端发送ChangeCipherSpecFinished 报文的同时就开始发送应用数据(Application Data)如 HTTP 请求,服务器端在 TLS 握手完成时直接返回应用数据,通俗来说就是抢跑,从而节约一次 RTT时间;但在 TLSv1.3协议中,直接优化了密钥协商机制,去掉了 ChangeCipherSpec,所以不需要 TLS False Start 特性,就可以减少一次 RTT;

另外一个提高 TLS 握手效率的机制是会话复用。会话复用的原理很简单,将第一次握手辛辛苦苦算出来的对称密钥存起来,后续请求中直接使用。这样可以节省证书传送等开销,也可以将 TLS 握手所需 RTT 减少到一个;会话复用有两种 会话标识符(Session Identifier) 和 会话记录单(Session Ticket)机制,Session Ticket 是推荐的做法,由于它是服务器把加密后的密钥信息保存在客户端,浏览器在 Hello消息报文中带上该加密信息,服务器能成功解密,就可以快速完成握手。

Session ID 是需要在服务器端保存对应的信息,失效时间不好控制,而且会占用大量服务器资源,另外,在负债均衡器情况下,多个服务器之间通常不会同步 Session 信息,如果两次请求不在同一个后台服务器上,就无法找到匹配的信息。

如下图,结合 TCP 的 TLS 协议握手过程的改进路径:从 TLSv1.2 完整的握手过程,到 TLS False Start 增强,再到 TLSv1.3加 Zero RTT 特性的会话复用。

Handshake,TLS over TCP

利用 AWS Lightsail 搭建 WordPress 测试环境

Amazon Lightsail 是面向开发人员构建应用程序或网站所需的一切,起步价 3.5 美金一个月,本文就直接利用 Lightsail的 WordPress 模板快速构建一个支持 TLS1.2 和 TLS1.3 的博客测试站点。3.5美金一个月,换算成人民币 8毛一天,跟我包月骑行价格差不多,可以得到什么的资源支持呢?

  • 1vCPU,512M内存
  • 20GB SSD 磁盘
  • 1 TB 数据传输额度(这个是最爱)
  • 免费的公网静态 IP 地址
  • 一键式 SSH 终端访问 (Web)

创建测试环境:

SSLLAbs

更新 Apache2 的配置文件,只启用 TLSv1.2 和 TLSv1.3,重启 Wordpress,通过 SSL Labs 测试可以打分到 A级;

# /opt/bitnami/apache2/conf/bitnami/bitnami.conf

SSLProtocol -all +TLSv1.2 +TLSv1.3
SSLProxyProtocol -all +TLSv1.2 +TLSv1.3
SSLHonorCipherOrder on
SSLCipherSuite "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+E
CDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH !aNULL !eNULL !LOW !3DES 
!MD5 !EXP !PSK !SRP !DSS !EDH !RC4"

接下来,我们利用 CURL 命令, 版本 7.52.0 之后支持 TLSv1.3,该测试 Wordpress 站点部署在韩国区域,我们又启动一台在新加坡的 Lightsail Linux 服务器进行测试,curl 命令的版本已经是 7.68.0,因此我们通过以下代码来测试 TLSv1.2 和 TLSv1.3 的性能差异:

#!/bin/sh

curl -o /dev/null --tlsv1.2 --tls-max 1.2 --no-sessionid --no-keepalive -s -w tls1.2_time_connect:"\t"%{time_connect}"\n"tls1.2_time_appconnect:"\t"%{time_appconnect}"\n" https://b.soldierxue.cn

curl -o /dev/null --tlsv1.3 --no-sessionid --no-keepalive --no-buffer -s -w tls1.3_time_connect:"\t\t"%{time_connect}"\n"tls1.3_time_appconnect:"\t"%{time_appconnect}"\n" https://b.soldierxue.cn

SSL/TLS处理时间统计,平均而言,从新加坡访问韩国站点,基于 HTTP/1.1, TLSv1.2 相比 TLSv1.3,消耗在握手协商过程的时长,约为 1.8 倍, 平均处理时长 22.3ms vs 12.25ms

TLSv1.2 TLSv1.3
22.2ms 12.3ms
22.6ms 12.1ms
22.7ms 12.1ms
22.1ms 12.2ms
22.5ms 12.4ms
22.1ms 12.4ms

TLSv1.3 在 Netflix 的测试和应用

奈飞团队在视频流用户体验优化中也对 TLSv1.3 进行了 A/B 测试,发现,在速度较慢或拥挤的网络上(可以用至少 0.75 的分位数表示),TLS 1.3 表现最优,但在所有网络条件下 TLSv1.3 都取得了不错的延迟改善。

Netflix的TLS AB测试结果

重新缓冲(media rebuffer)通常情况下,是由于 CPU 负载较高,设备处理视频流数据的速度不够快。与 TLSv1.2 进行对照实验,结果显示,采用 TLSv1.3 的单元重新缓冲提升了 7.4%。这一结果表明,将 TLSv1.3 与 0-RTT 结合使用效率更高,可以降低 CPU 负载。

奈飞团队已经开始在较新的消费电子设备上部署 TLSv1.3,预计在不久的将来部署更多支持 TLSv1.3 功能的设备。

申明

本站点所有文章,仅代表个人想法,不代表任何公司立场,所有数据都来自公开资料

转载请注明出处


公众号二维码

诞生于 2019,遇见 2020。

感谢关注,欢迎动动手指标星和置顶;

这样就不会错过少但精彩的技术探讨、团队建设、案例分享!

每周至少一更,转发是对我的最大鼓励!

学习之路漫漫,走走停停,
偶有所感,随心所记,
言由心声,问心无愧!

从客户中来,到客户中去!