跳到内容

第 4 卷 / 第 5 章 / 第 1 课

5.1 TLS 握手、证书链与安全边界 ​

TLS 建立的安全通道要回答三个问题:

  1. 路径上的观察者能否读懂内容?
  2. 内容被修改时,接收方能否发现?
  3. 对端是否真是客户端打算连接的身份?

TLS 分别用 confidentiality、integrity 和 peer authentication 回答它们。HTTPS 通常是 HTTP over TLS;“SSL”只是许多 API 中保留的历史名字,现代部署不应启用 SSLv2 或 SSLv3。

1. TLS 在协议栈的位置 ​

对 HTTP/1.1 和 HTTP/2 而言,常见路径是:

text
HTTP semantics
    ↓
HTTP/1.1 message framing 或 HTTP/2 frames
    ↓
TLS records
    ↓
TCP byte stream

TLS 不是 OSI 模型里只能归入某一层的固定答案,实践中把它看成 application protocol 与 transport 之间的安全层就够了。HTTP/3 的路径不同:QUIC 集成 TLS 1.3 handshake,使用 QUIC packet protection,不采用 TLS record layer。

2. 先看 TLS 1.3 的主线 ​

下图限定为证书认证的完整握手,没有 HelloRetryRequest、PSK 恢复或 client certificate。ALPN 的最终选择在 EncryptedExtensions 中,ServerHello 本身不返回整个协商结果。

text
Client                                             Server
  |                                                   |
  | ClientHello                                      |
  | supported_versions, key_share, SNI, ALPN  ------>|
  |                                                   |
  |<------ ServerHello, key_share                     |
  |        {EncryptedExtensions}                      |
  |        {Certificate}                              |
  |        {CertificateVerify}                        |
  |        {Finished}                                 |
  |                                                   |
  | {Finished} ------------------------------------->|
  |                                                   |
  |<========== protected application data ==========>|

花括号里的 message 已经受 handshake traffic key 保护。这是 TLS 1.3 的重要变化:ServerHello 之后的大部分 handshake 不再明文暴露。

ClientHello 带了什么 ​

  • supported_versions:客户端真正支持的 TLS 版本;
  • key_share:临时 (EC)DHE 公开参数;
  • SNI:希望访问的服务名,便于一个 IP 选对证书;
  • ALPN:希望在 TLS 里使用 h2 还是 http/1.1 等 application protocol。

ServerHello 选定版本、cipher suite 和 key share。双方根据 ephemeral Diffie–Hellman shared secret 和 handshake transcript 派生 key;临时私钥不是服务器 certificate private key。

可以用一个很小的 Diffie–Hellman 算例看共享值怎样相同:

python
p, g = 23, 5
alice_private, bob_private = 6, 15
alice_public = pow(g, alice_private, p)
bob_public = pow(g, bob_private, p)
alice_shared = pow(bob_public, alice_private, p)
bob_shared = pow(alice_public, bob_private, p)
assert alice_shared == bob_shared == 2
print(alice_public, bob_public, alice_shared)  # 8 19 2

这里的 23 只方便手算,暴力枚举即可破解,不能用于加密。真正的 TLS 使用标准化 group,并经 HKDF 和 transcript 派生不同用途的密钥;这个算例本身也没有身份认证,仍可被中间人替换公开参数。

身份证明与 Finished ​

Certificate 给出 server 为这次连接选择的 certificate chain。CertificateVerify 用 certificate private key 对 handshake context 签名,证明对端确实持有相应 private key。Finished 对 transcript 做 key-based verification,让双方检测 handshake 是否被篡改。

一次完整的 TLS 1.3 handshake 通常用 1 RTT 建立 application traffic keys。这不等于“打开网页只要 1 RTT”:DNS、TCP 或 QUIC 建连、HTTP redirect 和 resource loading 都可能增加往返。

如果 key share 不符合服务器要求,HelloRetryRequest 会增加一轮;恢复握手可能省略 Certificate/CertificateVerify,并依据 PSK 建立认证状态。也不能把图理解为服务器必须等客户端 Finished 才能发送任何应用数据,双方可用数据的时机应按具体握手状态判断。

3. Certificate path validation 不是“对上签名”就结束 ​

text
local trust anchor
        |
intermediate CA certificate
        |
leaf certificate for service identity

Server 通常发 leaf 和所需 intermediate certificates,不应把 root 当作必须发送的链尾。最后的 trust anchor 由 client 的 trust store 和 policy 决定;它不是因为“自签名”就自动可信。

通过 certificate path validation 只是验证的一部分。HTTPS client 至少还要检查:

  • certificate 的 validity period 覆盖当前时间;
  • subjectAltName 中的标识与期望 service identity 匹配;
  • key usage / extended key usage 允许它用于对应用途;
  • signature algorithm、key size 和 policy 可接受;
  • certificate 没有被 local policy 判定为 revoked。

这也是为什么不应在业务代码里自己实现 certificate validation。使用成熟 TLS library 的 secure defaults,再明确配置 trust store 和 service identity。

例如证书只有 DNS SAN *.example.test,它可覆盖 api.example.test,不能覆盖 example.test 或 a.b.example.test。访问 IP literal 时需匹配 IP SAN,不能把 DNS SAN 中看似 IP 的字符串当成同一种标识。RFC 9525 不再接受用 subject Common Name 代替 SAN。

4. Cipher suite 到底协商了什么 ​

TLS 1.2 的一个 suite 名可能包含 key exchange、authentication、bulk encryption 和 hash,例如 IANA 名称:

text
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
    |     |        |           |
 key ex  auth     AEAD       PRF hash

OpenSSL 也常把它显示为 ECDHE-RSA-AES128-GCM-SHA256。两种字符串不同,不代表协议不同。

TLS 1.3 的 suite 缩小了职责:

text
TLS_AES_128_GCM_SHA256

它选择 AEAD 和 HKDF 使用的 hash。Key exchange group 与 signature algorithm 在其他 extension 中独立协商。因此,不能从 TLS 1.3 suite 名里推断 server certificate 是 RSA 还是 ECDSA。

5. Forward secrecy 保护的是过去 ​

旧式 static RSA key exchange 中,如果攻击者录下 encrypted traffic,以后又拿到 server private key,就可能恢复过去的 session secret。Ephemeral (EC)DHE 为每次 handshake 产生临时 key material;长期 private key 用于 authentication,不直接变成 application traffic key。

这叫 forward secrecy。它假定 ephemeral secret 已及时销毁,也不会神奇地抵消 endpoint 当时已被控制的风险。TLS 1.3 的 certificate-based full handshake 使用 ephemeral (EC)DHE,不再支持 TLS 1.2 的 static RSA key exchange。

这个结论针对包含临时 (EC)DHE 的握手。TLS 1.3 也定义仅基于 PSK 的模式,不能把“版本是 1.3”直接等同于每种模式都具有相同的 forward secrecy。

6. 0-RTT 不是免费的加速 ​

TLS 1.3 在会话恢复时允许 client 发 early data,但 early data 可被 replay,它的 security properties 也与普通 application data 不同。

所以不要把“支付”“创建订单”“发放奖励”这类 non-idempotent operation 无条件放进 0-RTT。Server 要明确决定是否接收 early data,application 也要有 replay defense。

0-RTT 数据本身也不具备普通握手后数据相同的 forward secrecy。即使方法名叫 GET,若 endpoint 实际会产生副作用,或重放会产生不可接受的额外业务效果,也不能仅凭 Safe/Idempotent 标签放行;HTTP 服务可按 RFC 8470 用 425 Too Early 要求在握手后重试。

7. TLS 不解决什么 ​

  • certificate 通常验证 service identity,不验证登录用户的业务权限;
  • endpoint 中毒时,plaintext 在加密前或解密后仍可被偷走;
  • TLS 不防 SQL injection、XSS、CSRF 或越权;
  • 对端 IP、traffic timing 和 volume 等 metadata 仍可观测;SNI 是否隐藏取决于 ECH 部署,不能默认已隐藏;
  • TLS 不保证 service availability。

8. 常见误解 ​

“有锁图标就说明网站可信。” 它只说明 browser 成功建立了与该 service identity 的 protected connection,不代表这家站点的商业行为值得信任。

“Server 发来的 root certificate 证明了自己。” Trust 来自 client 预先配置的 trust anchor 和 policy,不来自对端自己递上来的 root。

“TLS 1.3 的 AES suite 说明 certificate 一定是 RSA。” TLS 1.3 把 AEAD suite 与 signature algorithm 分开协商。

“把 certificate verification 关掉,content 还是 encrypted,所以可以上线。” Encryption 没有 authentication 时,client 可能正在和 attacker 建立一条完美加密的通道。

9. 练习与验收 ​

  1. 按顺序说出 TLS 1.3 中 Certificate、CertificateVerify 和 Finished 各自证明了什么。
  2. 为什么 server private key 泄露不一定能解开过去录下的 TLS 1.3 full-handshake traffic?
  3. 用自己的话区分“certificate path valid”和“service identity matches”。
  4. 列出两个不应无条件放入 0-RTT 的 operation,说明共同原因。

实际故障往往只返回 CERTIFICATE_VERIFY_FAILED,不会直接说明是缺 intermediate、名称不匹配还是时间错误。下一课进入5.2 TLS 安全客户端与故障诊断,把这些原因逐项分开。

参考 ​

Built with VitePress | Software Systems Atlas