第4章 TLS 1.3 握手核心变化
TLS 1.3 在 2018 年正式落地(RFC 8446)。说实话,它是我见过最"激进"的一次协议升级。握手从 2-RTT 砍到 1-RTT,还搞出了 0-RTT 这种"先上车后补票"的玩法。更关键的是,它把降级攻击这条路堵死了。
这章咱们就聚焦三个核心变化:1-RTT 握手、0-RTT 原理、降级防护机制。每个点我都会结合项目里的实际案例来讲,希望能帮你少踩几个坑。
4.1 1-RTT 握手 — 一次往返就够了
TLS 1.2 握手要 2-RTT。你想想看,第一个 RTT 花在 ClientHello 和 ServerHello 上,第二个 RTT 花在证书和密钥交换上。用户等得心焦,老板催得头疼。
TLS 1.3 怎么做到的?说白了,它把密钥交换提前了。客户端在 ClientHello 里直接带上 key_share 扩展,把自己支持的 ECDHE 参数扔过去。服务器拿到后,直接在 ServerHello 里把密钥算好返回。一次往返,密钥就协商完了。
核心变化: TLS 1.3 移除了静态 RSA 和 DH 密钥交换,强制使用 ECDHE。所有密钥协商都在 1-RTT 内完成。
我在项目中遇到过一个问题——升级 TLS 1.3 后,有些老库的 key_share 扩展实现不完整,导致握手失败。嗯,这里要注意:客户端必须至少支持一个服务器认可的 ECDHE 曲线,否则握手会回退。
下面这张图展示了 TLS 1.3 1-RTT 握手的完整流程:
个人建议: 生产环境启用 TLS 1.3 时,先做灰度测试。我遇到过某些 CDN 节点对 supported_versions 扩展的解析有问题,导致部分用户握手失败。
4.2 0-RTT 原理 — 快,但有代价
0-RTT 是 TLS 1.3 的"大招"。客户端在第一个消息里就能带上加密数据。零往返等待,听起来很完美对吧?
原理其实不复杂。客户端在之前的握手中缓存了服务器的 session_ticket,里面包含了预共享密钥(PSK)。下次连接时,ClientHello 里直接带上 PSK 扩展,然后就能发送 Early Data(early_data 扩展)。
但代价也很明显——重放攻击。因为服务器在验证 Client Finished 之前就已经处理了 Early Data,攻击者可以截获并重放这个数据包。
我曾经踩过的坑: 在一个支付场景中,我们尝试用 0-RTT 加速首次请求。结果测试发现,同一个 early_data 被重放了 3 次,导致订单重复创建。后来我们只在幂等的 GET 请求上启用了 0-RTT,写操作一律不用。
适用场景其实很明确:
- 适合: 静态资源加载、查询接口、预拉取操作
- 不适合: 支付、下单、转账等写操作
- 谨慎使用: 登录态校验、 刷新
0-RTT 的流程可以概括为三步:
- 客户端发送 ClientHello + PSK 扩展 + Early Data
- 服务器验证 PSK,处理 Early Data,返回 ServerHello + Finished
- 客户端验证 Finished,开始正常通信
关键指标: 0-RTT 在首次连接时没有收益,只有在复用 session_ticket 时才能生效。session_ticket 的有效期通常为 4~8 小时,我建议根据业务场景自定义过期时间。
4.3 降级防护机制 — 防的是「假装我很老」
降级攻击是什么?攻击者篡改握手消息,让客户端和服务器以为对方只支持老版本,然后被迫用 TLS 1.1 甚至 SSL 3.0 通信。老版本的漏洞就多了去了。
TLS 1.3 在降级防护上下了狠手,主要靠三个手段:
| 防护手段 | 实现方式 | 效果 |
|---|---|---|
| 版本号检查 | ServerHello 中的 version 字段必须为 0x0304 |
客户端校验版本号,不匹配就终止握手 |
| 降级 SCSV | ServerHello 中包含 downgrade_protection 标记 |
客户端检测到降级标记后立刻断开 |
| 签名算法约束 | 证书签名必须使用 TLS 1.3 指定的算法套件 | 防止攻击者用弱签名算法伪造证书 |
你想想看,攻击者想干的事就是"骗"。骗客户端说服务器只支持 TLS 1.1,骗服务器说客户端只支持 TLS 1.1。TLS 1.3 的做法是:在 ServerHello 里写死版本号,客户端收到后一比对,发现有猫腻就直接拒了。
避坑指南: 我曾经在调试一个嵌入式设备时发现,它的 TLS 栈没有校验 ServerHello 的 version 字段。攻击者构造了一个 version=0x0301 的 ServerHello,设备就傻乎乎地用 TLS 1.0 通信了。嗯,这个漏洞的 CVSS 评分是 9.1。
降级防护的另一个重点是签名算法。TLS 1.3 要求证书签名必须使用 rsa_pss_rsae_sha256 或更强算法。老旧的 rsa_pkcs1_sha1 直接被拉黑。这个细节在迁移时很容易被忽略——有些 CA 签发的证书还是 SHA1 签名,在 TLS 1.3 下直接握手失败。
我的习惯: 每次启用 TLS 1.3 前,我都会用 openssl s_client -tls1_3 -connect host:port 测一遍握手。如果返回 "no protocols available",多半是证书签名算法不兼容。
最后说一句实在的:TLS 1.3 的降级防护不是"锦上添花",而是"必选项"。没有它的保护,中间人攻击的成功率会高出一个数量级。嗯,这点怎么强调都不过分。