TLS 1.3 设计目标:性能提升、安全强化与精简握手

这一章,我们来聊聊 TLS 1.3 的设计目标。

说白了,就是当年 IETF 那帮人为什么要推倒重来。

我自己参与过几次 TLS 1.2 的性能调优,那感觉就像是在一台老旧的机器上不断打补丁。每次握手都多一个来回,每个加密套件都留一个坑。到了 1.3,设计思路彻底变了——他们定了三个核心目标:性能要快、安全要硬、握手要简。

2.1 性能提升:把延迟打下来

先说性能。

我记得有一次给一个移动端 App 做优化,后台 API 全是 HTTPS。用户反馈说“加载太慢”。我抓包一看,好家伙,一个完整的 TLS 1.2 握手,加上证书链验证,网络往返(RTT)花了 3 到 4 个来回。在弱网环境下,光握手就干掉了 2 秒。

TLS 1.3 的核心思路是:把握手从 2-RTT 压缩到 1-RTT,甚至 0-RTT。

说白了,就是减少一次往返。1-RTT 是常态,0-RTT 是红包。
  • 1-RTT 握手:这是 TLS 1.3 的默认模式。客户端一发 ClientHello,服务端直接回复 ServerHello + 证书 + Finished,一个来回搞定。
  • 0-RTT 握手:针对之前连接过的用户。客户端可以带着上次缓存的密钥,直接在第一个包就发数据。我称之为“免检通道”。

你想想看,一个 RTT 省下来,在 4G 或者卫星链路上,可能就是 200 毫秒的差距。这在用户体验上是质变。

性能维度 TLS 1.2 TLS 1.3
首次连接(Full Handshake) 2-RTT 1-RTT
恢复连接(Resumption) 1-RTT 0-RTT / 1-RTT
加密延迟 协商后加密 握手阶段即加密
这里有个坑要注意:0-RTT 设计虽然快,但存在重放攻击的风险。我的建议是,只在幂等操作上使用 0-RTT,比如 GET 请求、预加载资源。千万别用在转账或者删除操作上。

2.2 安全强化:砍掉历史包袱

安全这块,TLS 1.3 做得挺绝的。

TLS 1.2 时代,支持了一大堆加密套件,什么 CBC 模式、RC4、3DES、静态 DH 密钥交换。当年兼容性是好,但这些年被爆出的漏洞,几乎都出在这些老古董身上。比如 CBC 的 padding oracle 攻击、RC4 的偏差、3DES 的 64 位块大小带来的 Sweet32 攻击。

我在项目中曾经排查过一个诡异的安全扫描报告,上面报了“TLS 1.2 使用 CBC 模式”。我花了整整一天去配置哪些套件要禁用,哪些要优先。简直是噩梦。

TLS 1.3 的做法很简单:把所有不安全的、过时的、有争议的算法,直接扔出去。

具体体现在:

  • 删掉了 RSA 密钥交换:只保留 PFS(完美前向安全性)的 Diffie-Hellman 和 ECDHE。
  • 删掉了 CBC 模式:对称加密只用 AEAD 模式(如 AES-GCM, ChaCha20-Poly1305)。
  • 删掉了压缩:CRIME 和 BREACH 攻击都是通过压缩实现的信息泄露,直接砍掉最省事。
  • 删掉了静态 DH 和静态 ECDH:所有密钥交换都是临时的,保证每次会话密钥唯一。
这里我要特别提醒一点:TLS 1.3 的安全是拿兼容性换的。如果你还在用老旧的 F5 设备或者某些嵌入式设备,可能不支持 1.3。我曾经遇到过一台银行的老式 ATM 前置机,居然还强制要求 TLS 1.0。嗯,迁移前一定要做好兼容性测试。

2.3 精简握手:代码量少,出错的概率就低

最后一个设计目标,是精简握手流程。

我个人特别喜欢 TLS 1.3 的这个改动。因为握手越复杂,出错的可能就越多,调试起来也越痛苦。

先看 TLS 1.2 的握手:

Client                                      Server

ClientHello  -------->
                <--------  ServerHello
                           Certificate*
                           ServerKeyExchange*
                           CertificateRequest*
                <--------  ServerHelloDone

ClientKeyExchange -------->
CertificateVerify*
ChangeCipherSpec
Finished        -------->
                <--------  ChangeCipherSpec
                           Finished

一个握手下来,至少 4 个网络包。而且中间还夹杂着 CertificateRequest、ServerKeyDone 这些可选步骤。逻辑分支很多。

再看看 TLS 1.3:

Client                                      Server

ClientHello
(key_share)     -------->
                <--------  ServerHello
                           (key_share)
                           EncryptedExtensions
                           Certificate
                           CertificateVerify
                           Finished
Finished        -------->

看到了吗?消息数量直接减半。

为什么会这样?因为 TLS 1.3 把密钥交换和身份认证合并了。客户端在第一个包就带上自己的密钥份额,服务端在第一个回复里就完成所有认证。不需要单独的 ChangeCipherSpec,不需要反复确认。

核心变化有几点:

  • 移除 ChangeCipherSpec:这个字段在 1.2 时代是用来切换加密状态的,但经常被中间人利用。现在直接废弃。
  • 合并密钥交换与认证:握手消息被设计成“一条链”,谁在前谁在后都定死了,没法篡改。
  • 简化扩展机制:虽然 TLS 1.3 依然用扩展,但删掉了大量没用的扩展类型,只保留了必要的。
我在给 Nginx 迁移 TLS 1.3 时,遇到过旧版本的 SNI 扩展不兼容的问题。当时排查了很久,最后发现是客户端发送的 SNI 格式没更新。所以,我建议你在迁移时,先看一下服务端日志里的“不支持扩展”报错,八成能定位问题。

2.4 一张图看懂设计目标

下面这张 SVG 图,把刚才讲的三个目标串起来。你可以把它当作思维框架图。

TLS 1.3 设计目标 TLS 1.3 设计目标 性能提升 减少 RTT 延迟 安全强化 移除不安全算法 精简握手 减少消息轮次 1-RTT 0-RTT 只支持 AEAD PFS 强制 移除 RSA 移除 CBC 移除 CCS 合并消息 简化扩展 三者相辅相成:性能靠精简握手,安全靠强制算法

这张图是我自己画的手稿风格。你看,性能、安全、精简,这三者是互相勾连的。性能提升靠的是精简握手流程,而安全强化靠的是强制使用更少但更强的算法。

2.5 这些目标对迁移的影响

最后,我想聊聊这三个目标对我们迁移工作的实际影响。

  • 因为性能提升:实际迁移后,你会看到用户首屏加载时间缩短 10%-30%。尤其是在 3G/4G 网络下,效果明显。我个人在某个 CDN 项目上测试过,0-RTT 恢复连接后,平均请求延迟降了 150ms。
  • 因为安全强化:你需要更新你的密码学库。OpenSSL 1.1.1 以上版本才支持 TLS 1.3。如果你的中间件用的是旧版 OpenSSL 或者 OpenJDK 8,注意要升级到 u261 以上。
  • 因为精简握手:消息少了,但每个消息的解析规则变了。我之前在调试 Wireshark 抓包时发现,1.3 的 Finished 消息不加密,很多人第一次看到会以为抓包有问题。其实那个是设计如此。
这里有个经典的坑:有些中间件设备(如某些老款 F5 负载均衡)会“自作聪明”地修改 TLS 握手消息。在 1.2 时代,它们可能没事。但在 1.3 时代,因为消息合并和签名验证更严格,这种修改会导致握手失败。所以我建议你迁移时,先关掉所有“SSL 加速”或者“SSL 穿透”的中间件功能,纯透传测试一遍再开启。