TLS/SSL 协议深度解析:握手过程、证书链验证、密码套件选择、TLS 1.3 新特性

说到加密接口安全,TLS/SSL 是绕不开的基础设施。我做过不少审计项目,发现很多团队虽然配了证书、启用了 HTTPS,但握手细节和证书链验证经常被忽略。说白了,配置错了等于没配。今天咱们就把这块硬骨头啃下来。

4.1 握手过程:不是你想的那么简单

TLS 握手是整个协议的核心。它的目标就三个:身份认证、密钥协商、加密开始。你想想看,客户端和服务端之前互不相识,怎么就能安全地交换密钥呢?

4.1.1 传统 TLS 1.2 握手(完整版)

先看 TLS 1.2 的完整握手。我记得第一次抓包分析这个流程时,整整看了一天。

  1. ClientHello:客户端发个招呼,带上支持的 TLS 版本、密码套件列表、一个随机数。
  2. ServerHello:服务端选一个密码套件、发自己的随机数,顺便把证书甩过来。
  3. Certificate:服务端证书链,包含服务器证书、中间证书,有时还会带上根证书。
  4. ServerKeyExchange:如果密码套件需要(比如 DHE 或 ECDHE),服务端会发送密钥交换参数。
  5. ServerHelloDone:服务端说“我说完了,轮到你”。
  6. ClientKeyExchange:客户端根据服务端的参数,生成预备主密钥,加密后发给服务端。
  7. ChangeCipherSpec:双方各自切换加密策略,后续开始加密通信。
  8. Finished:发送一条加密后的消息,验证握手没被篡改。

这里有个容易踩的坑——ServerKeyExchange 这一步。如果是 RSA 密钥交换,这一步可以没有。但如果是 ECDHE,服务端必须发公钥和签名。我在项目中遇到过开发直接写死 RSA,结果安全审计时被判定为“前向安全性不足”。

注意:RSA 密钥交换不支持前向安全性。一旦私钥泄露,之前的所有通信记录都能被解密。建议全量使用 ECDHE 或 DHE。

4.1.2 握手流程可视化

光讲文字有点干,我画了一张图帮你直观理解。

TLS 1.2 / 1.3 握手对比流程图 TLS 握手流程对比(1.2 vs 1.3) TLS 1.2 完整握手 客户端 ClientHello (等待中) ClientKeyEx ChangeCipher Finished 服务端 ServerHello Certificate ServerDone ChangeCipher Finished TLS 1.3 简化握手 客户端 服务端 ClientHello (等待) Finished ServerHello Certificate Finished TLS 1.3 将握手从 2-RTT 降为 1-RTT,去掉 ServerKeyExchange、ChangeCipherSpec 等冗余步骤 支持 0-RTT 模式(早期数据),但需防范重放攻击
我的习惯:抓包分析时,我会重点关注 ServerHello 返回的密码套件是否在客户端的支持列表中。如果服务端降级到了弱套件,那肯定有问题。

4.2 证书链验证:别只看叶子证书

很多人觉得证书验证就是检查证书是否过期。其实远不止这些。我见过一个真实案例——某个金融 App 上线前审计,发现证书链中间证书居然在本地存储中硬编码了私钥。嗯,这可是大事故。

4.2.1 证书链是什么?

证书链就像是一份担保书:

  • 叶子证书:你的网站域名,由中间 CA 签发。
  • 中间证书:由根 CA 签发,负责代理签发叶子证书。
  • 根证书:预装在操作系统或浏览器中的信任锚点。

说白了,浏览器不会直接信任任何证书。它必须通过根证书这个“老祖宗”来验证整条链。如果链断了,浏览器会跳警告。

4.2.2 验证步骤

  1. 签名验证:从叶子证书开始,用上一级证书的公钥验证本证书的数字签名。
  2. 有效期检查:每个证书的生效时间需覆盖当前时间。
  3. 域名匹配:叶子证书的 CN 或 SAN 必须和访问的域名一致。
  4. 吊销检查:通过 CRL 或 OCSP 确认证书未被吊销。

我记得有一次在审计一个海外部署项目时,发现他们的根证书是自签的。开发说“本地测试没问题啊”。但实际上,移动端和浏览器根本无法信任自签根证书。正确的做法是使用受信任的公共 CA 或搭建内部 CA 并预先分发根证书。

关键点:证书链的深度不能超过协议规定的最大值(通常是 10 级)。我在项目中遇到过深度为 7 的链,某些老旧设备直接不认,导致握手失败。

4.3 密码套件选择:不是越新越好

密码套件决定了加密的强度、密钥交换的方式以及消息认证的算法。我建议你把它当成“菜单”——选错了菜,可能消化不良。

4.3.1 密码套件结构

一个典型的密码套件长这样:TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384

字段 含义 我个人的推荐
TLS 协议 固定
ECDHE 密钥交换算法 优先 ECDHE 或 DHE
RSA 身份认证算法 RSA 或 ECDSA(ECDSA 更轻量)
AES_256_GCM 对称加密算法及模式 AEAD 模式(GCM、CCM)
SHA384 消息认证码哈希算法 SHA256 或以上
避坑指南:我曾经在配置中看到有人启用了 RC4 和 3DES 套件,因为他们说“兼容性更好”。千万别用!RC4 已被彻底攻破,3DES 也即将退役。安全审计第一件事就是禁用这些弱套件。

4.3.2 推荐的密码套件清单

  • TLS_AES_128_GCM_SHA256(TLS 1.3 默认)
  • TLS_AES_256_GCM_SHA384(TLS 1.3 高安全)
  • TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256(TLS 1.2 推荐)
  • TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384(TLS 1.2 高安全)

4.4 TLS 1.3 新特性:不是简单的升级

TLS 1.3 在 2018 年正式发布,它把老版本的很多历史包袱全扔了。我特别喜欢它的一点——握手从 2 次往返降到 1 次。你想想看,在弱网环境下这能省多少时间。

4.4.1 主要变化

  • 移除不安全算法:RSA 密钥交换、RC4、3DES、CBC 模式全被删除。
  • 使用 AEAD 加密:只允许 GCM、CCM、ChaCha20-Poly1305 这类认证加密算法。
  • 握手简化:合并 ServerHello 和 Certificate,去掉 ServerKeyExchange、ChangeCipherSpec。
  • 0-RTT 模式:客户端可以在第一次消息中就携带应用数据,适合短连接场景。

4.4.2 0-RTT 的隐患

0-RTT 虽然快,但有重放攻击的风险。攻击者如果截获了客户端的 0-RTT 数据,可以多次发送给服务器。我在审计一个 IoT 平台时发现它们的传感器上报数据用了 0-RTT,结果重复数据导致后端的幂等性处理失效。

我的建议:如果使用 0-RTT,务必在应用层做好防重放设计,比如添加 Nonce 或时间戳验证。TLS 1.3 本身不替你处理重放,它只保证通道安全。

4.4.3 迁移建议

如果你还在用 TLS 1.2,建议逐步向 1.3 迁移。但不是所有客户端都支持 1.3。我一般策略是:

  • 服务端同时开启 1.2 和 1.3。
  • 1.2 只保留 ECDHE + AES-GCM 的高强度套件。
  • 1.3 启用并禁用所有向后兼容的降级机制。

好了,这一节的深度解析就到这里。要记住,TLS 不是配完证书就完事的。握手细节、证书链、密码套件选择,每一项都会影响你的接口安全等级。下次遇到加密接口审计,不妨从这些维度入手,你会发现很多被忽视的漏洞。