跳到内容

第 8 卷 / 第 2 章 / 第 2 课

2.2 TLS 1.3 与证书自动化:从握手到轮换 ​

TLS 建立会话密钥,按选定模式认证服务端或双方,并保护后续记录。它不是“用证书公钥把 HTTP 加密”:TLS 1.3 通常用临时 (EC)DHE 建立共享秘密,证书私钥负责给握手上下文签名。

TLS 1.3 握手绑定协商结果 ​

以下是证书认证、无客户端证书、无 HelloRetryRequest 和 early data 的简化完整握手;PSK 恢复模式的消息会不同:

text
ClientHello
  supported_versions, cipher_suites, key_share, SNI, ALPN
        ↓
ServerHello + key_share
        ↓  双方派生握手密钥
EncryptedExtensions
Certificate
CertificateVerify  ← 服务端用证书私钥签名握手 transcript
Finished           ← 证明握手密钥与 transcript 完整
        ↓  客户端验证服务端
Client Finished    ← 客户端确认握手 transcript
        ↓
后续记录使用应用流量密钥

证书验证回答“这个公钥是否被信任用于目标服务名”,CertificateVerify 回答“对端是否持有对应私钥”,Finished 把此前协商内容和派生密钥绑定起来。这是该证书认证流程中的职责分工;PSK 握手可以不发送证书,而以已建立的 PSK 信任完成认证。

TLS 1.3 cipher suite 只选择 AEAD 与 hash,密钥交换和签名算法在独立扩展中协商。这与 TLS 1.2 的套件命名不同,不能用旧的字符串检查逻辑判断全部安全属性。

SNI 与 ALPN 参与服务选择 ​

SNI 告诉共享地址上的服务端客户端要访问哪个 hostname,服务端据此选择证书和虚拟主机;ALPN 协商 HTTP/2、HTTP/1.1 等应用协议。两者配置错误会导致拿错证书、路由到错误租户或协议降级。

SNI 在传统 TLS 1.3 ClientHello 中通常可见,TLS 本身不隐藏访问的域名。需要保护这一元数据时,要关注 ECH 的实际客户端、DNS 和服务端生态支持,即使成功使用 ECH,目标 IP、流量大小和时间特征仍可能可见。SNI 参与选证书,本身不是客户端完成 hostname 验证的证据。

0-RTT 以重放风险换取延迟 ​

恢复会话时,TLS 1.3 可以让客户端在完整握手前发送 early data。0-RTT 不具备一般意义上的重放保护,攻击者可能让服务端多次接收同一请求。

因此转账、创建订单、修改权限、发放凭据等非幂等操作不应接受 0-RTT。即使 GET 被设计为幂等,也要确认重放不会导致重复计费、一次性 token 消耗或不可接受的审计负荷;正常的重复访问日志并不自动违反 HTTP 幂等语义。防重放需要应用与 TLS 终止层共同设计,不能仅靠库开关。

mTLS 认证的是证书主体,不是业务授权 ​

双向 TLS 中,服务端也请求客户端证书、验证其链与身份,并验证客户端的 CertificateVerify 持钥证明。它适合受控设备、服务间身份和高保证管理接口,但仍需映射:

text
validated certificate identity
       → workload / device principal
       → authorization policy
       → permitted resource and action

“来自受信 CA 的任意证书”通常过宽。要检查 SAN/URI identity、EKU、签发层级、环境和吊销状态,并限制哪个 issuer 能为哪个命名空间发证。连接池与长连接可能继续使用早先建立的会话,不会因为磁盘证书换新就自动重验身份;必要时需要连接寿命限制、重新认证或主动关闭,纳入撤销窗口分析。

ACME 把域名控制验证变成协议 ​

ACME 的核心对象关系:

text
Account → Order → Authorization → Challenge → Finalize CSR → Certificate

HTTP-01、DNS-01 等 challenge 证明申请者能完成对应域名控制验证,不证明网站经营者可信、合法或没有恶意内容。DNS-01 适合通配符和不直接暴露 HTTP 的服务,但 DNS API 凭据往往权限很大;应限制到特定 zone/record、使用短期凭据并隔离签发控制器。

证书自动化不是一个 cron:

  1. 提前续签并加入随机抖动,避免同一时刻风暴;
  2. 将匹配的证书与私钥作为同一版本发布,例如先写入新目录,再切换版本指针;分别替换两个文件不等于原子发布;
  3. 让进程 reload 或热更新,并验证实际 listener 已加载新 serial;
  4. 保留短暂重叠以支持连接和多副本滚动;
  5. 从外部探针检查链、SAN、协议和剩余期限;
  6. 对签发、challenge、reload 和握手错误分别告警。

只监控磁盘上的 .pem 到期日,会漏掉负载均衡器仍在提供旧证书的情况。

检查真正提供服务的监听端口 ​

下例要求目标已经提供 TLS 服务,并用 roots.pem 明确指定本次测试信任的根。将连接地址替换成实际测试地址;若直连某个后端 IP,仍保留要验证的服务名。

bash
openssl s_client -connect api.example.com:443 \
  -servername api.example.com -verify_hostname api.example.com \
  -verify_return_error -CAfile roots.pem -tls1_3 \
  -alpn h2,http/1.1 </dev/null

-servername 发送 SNI,-verify_hostname 检查预期身份,-verify_return_error 让验证错误终止握手;三者不能互相替代。核对输出中的协议、协商 ALPN 与验证结果;把返回的 PEM 证书保存为 served-leaf.pem 后,可用 openssl x509 -in served-leaf.pem -noout -serial -dates 检查序列号与有效期。再把 hostname 改错,确认握手失败。强制 -tls1_3 只证明这一轮测试支持 TLS 1.3,不证明服务器已经禁用旧协议。

私有 PKI 要限制故障影响范围 ​

服务网格或工作负载身份平台常签发数小时到数天的短期证书。短生命周期降低静态凭据长期泄露风险,却把可用性依赖转移到 issuer、身份 attestation、时钟和轮换代理。

根密钥应离线或严密保护,在线 intermediate 按环境、区域或用途隔离。轮换 CA 需要 trust bundle 先加入新根、再迁移签发层与叶子、等待旧凭据和会话退出后移除旧根的重叠阶段;直接替换根会把整个集群切断。

TLS 生产基线 ​

  • 优先 TLS 1.3;需要 TLS 1.2 兼容时按 BCP 195 限制协议与套件;
  • 禁用证书校验失败后的默认继续路径;
  • 使用提供前向保密的密钥交换并安全管理 session ticket key;
  • 明确是否启用 0-RTT,以及哪些请求可以接受;
  • 对外部与内部服务都验证完整链、reference identity 和用途;
  • 自动签发、续期、部署、reload 与外部验证连成可复验流程;
  • 监控握手失败率、协议版本、证书剩余期限和异常新证书。

下一章从服务身份转向用户认证与会话,再学习 OAuth/OIDC 和授权策略。

参考资料 ​

Built with VitePress | Software Systems Atlas