TLS 握手协议详解

TLS 握手,说白了就是客户端和服务器在正式传输数据前,先互相打个招呼、亮个身份、商量好怎么加密的一个流程。很多开发者觉得这步就是走个过场,但其实它直接决定了整个会话的安全基础。我见过不少安全事件,根源就是握手阶段没处理好。

握手到底在做什么?

咱们可以把它想象成两个陌生人第一次见面:

  • 先打招呼:Client Hello / Server Hello,交换随机数、支持的密码套件、TLS 版本。
  • 亮证件:服务器发送证书,证明自己是它声称的那个人。
  • 对暗号:客户端验证证书,然后双方用自己的私钥 / 公钥完成密钥交换,生成一个只有他俩知道的会话密钥。
  • 最后确认:双方互发一个“Finished”消息,握手结束,之后的数据全部用会话密钥加密传输。

这里有个点容易忽略:TLS 1.3 和 TLS 1.2 的握手流程差别很大。1.3 把很多步骤合并了,少了一次往返(1-RTT),甚至支持 0-RTT。我建议审计的时候先确认实际用的是哪个版本,再对照标准流程检查。

完整握手流程(TLS 1.2 为例)

下面这张流程图,是我从一个真实项目中总结出来的。你把它套到包里,基本不会走偏。

TLS 1.2 完整握手流程 Client ① ClientHello 验证服务器证书 生成 Pre‑Master Secret ④ ClientKeyExchange ChangeCipherSpec ⑥ Finished 应用数据 (加密传输) Server ② ServerHello ③ ServerCertificate ServerHelloDone ⑤ 解密 Pre‑Master ChangeCipherSpec ⑥ Finished 应用数据 (加密传输)
顺便提一句:这套流程里,“ChangeCipherSpec”虽然单独画了一个框,实际上是协议层的一个独立消息。我在审计时见过不少实现把它跟 Finished 合并了,严格来说不合规。但大多数场景下不会出问题,只有做合规审计时才需要抠这个细节。

关键步骤拆解

1. ClientHello 和 ServerHello

客户端把支持的密码套件列表扔给服务端,服务端挑一个。这个挑选过程,我曾经遇到过一个坑:服务端没有按照客户端首选项选,而是选了一堆过时的低强度套件,结果被扫出弱加密漏洞。

审计时一定要抓包对比:服务端选择的套件是不是在客户端列表里?是不是满足当前安全策略的最低强度要求?

2. 证书验证

客户端收到服务器证书后,会做几件事:

  • 检查证书链是否完整、是否受信任
  • 检查证书有效期(容易被忽略的是,有些服务器用已过期的证书依然能握手成功,只是用户端会弹出警告)
  • 检查证书 CN 或 SAN 是否匹配访问的域名
  • 检查证书吊销状态(OCSP / CRL)
注意:很多客户端并不会强制检查证书吊销!如果你在做内部 DSPM 审计,建议手动验证一下服务端是否支持 OCSP Stapling,以及客户端是否有软性跳过吊销检查的行为。

3. 密钥交换

常见方式有两种:RSA 密钥交换和 (EC)DHE 密钥交换。RSA 方式里,客户端直接用服务器公钥加密 Pre‑Master Secret。这种方式简单,但有个致命问题——没有前向安全性:一旦服务器私钥泄露,之前所有会话都能被解密。

所以我个人习惯,在审计报告里强烈推荐 (EC)DHE。TLS 1.3 已经彻底废弃了 RSA 密钥交换,但很多存量 TLS 1.2 还在用。

密钥交换算法前向安全性TLS 1.3 支持审计建议
RSA否否禁止使用
DHE是否(但在 1.2 中可用)允许,但需要参数 ≥ 2048 位
ECDHE是是推荐使用

4. Finished 消息与验证

握手最后一步,双方用协商好的会话密钥加密一条 Finished 消息发给对方。这条消息的内容是基于此前所有握手消息的哈希值。如果对方解不开或者哈希不匹配,握手就会失败。

嗯,这里要注意:Finished 消息是第一条被加密传输的数据。我之前审计过一个 SDK,它在 Finished 之前就开始发应用数据了,这是个严重缺陷,攻击者完全可以注入伪造数据。

TLS 1.3 的简化

TLS 1.3 把上面的握手从 2-RTT 压缩到 1-RTT(甚至 0-RTT),主要是靠“提前给出密钥共享参数”。流程变成了:

  1. 客户端直接发送 ClientHello + 密钥交换的 share
  2. 服务端回复 ServerHello + 自己的 share + 证书 + 直接进入加密状态
  3. 双方几乎同时算出会话密钥,加密 Finished

审计时要注意:TLS 1.3 不支持很多老密码套件,比如 RSA 密钥交换、CBC 模式等,如果应用层代码里硬编码了某些套件,可能导致握手失败。我亲手帮一个客户查过一个线上故障,就是因为他们服务端配置了 TLS 1.3 但密码套件列表里还留着只有 1.2 才用的套件,结果客户端选择困难,干脆降级成 1.2 了。这不算是漏洞,但性能差了不少。

审计核心要点总结:
  • 版本协商:是否允许 TLS 1.0/1.1?通常应该禁用。
  • 证书链:是否完整、是否信任、是否吊销。
  • 密钥交换:是否使用 (EC)DHE,参数强度是否够。
  • 密码套件:是否包含弱套件(如 RC4、CBC-MAC、3DES、export 级)?
  • Finished 验证:报文顺序和加密时机是否合规。