第2章:TLS/SSL 协议基础回顾
聊优化之前,咱们得先把地基夯实。TLS 会话复用和 OCSP Stapling 说白了都是在标准握手流程上做文章。你不理解正常的握手,就很难明白优化到底省了哪些步骤。
我在好几年前接手过一个电商平台的项目,当时线上偶尔报 SSL 握手超时。我一开始也蒙了,以为是证书配置有问题。后来抓包一看——嗯,原来是每次请求都走完整握手,客户端和服务器来回折腾了好几个 RTT,网络稍一抖动就超时了。那是我第一次深刻体会到,理解握手细节有多重要。
2.1 握手协议的核心流程
咱们先回顾最经典的 TLS 1.2 完整握手。一个连接建立,客户端和服务器要交换不少东西。
- ClientHello:客户端发个问候,带上支持的 TLS 版本、密码套件列表、一个随机数。
- ServerHello:服务器选好版本和套件,也发回一个随机数。
- Certificate:服务器把自己的证书链发给客户端。我建议你多关注这个步骤,证书的尺寸有时候能拖慢整个流程。
- ServerKeyExchange:根据密码套件不同,可能需要额外的密钥交换参数。
- ServerHelloDone:服务器表示“我说完了”。
- ClientKeyExchange:客户端生成预主密钥,用服务器的公钥加密后发过去。
- ChangeCipherSpec + Finished:双方切换加密方式,并验证握手消息的完整性。
这个流程下来,通常需要 2 个 RTT(往返时间)。如果客户端和服务器物理距离很远,比如跨国访问,光握手就得几百毫秒。
到了 TLS 1.3,握手被压缩到了 1 个 RTT。我记得第一次在实验室验证 1-RTT 流程时,确实觉得很爽。它把 ServerHello 和证书合并了,真正的密钥交换也提前了。但即便如此,在弱网环境下,1 个 RTT 也很宝贵。
2.2 会话复用:省掉重复握手
你想想看,每次重新连接都走一遍完整握手,是不是很浪费?尤其是移动端 APP,频繁切换网络、断线重连,简直就是噩梦。
会话复用就是解决这个问题的。它允许客户端和服务器在后续连接中,复用之前协商好的会话参数。主要有两种机制:
- Session ID(会话标识):服务器端保存会话状态,客户端保存一个 ID。重连时发这个 ID,服务器查到缓存就直接恢复。
- Session Ticket(会话票据):服务器把会话状态加密成票据发给客户端,由客户端保存。重连时客户端提交票据,服务器解密后恢复。
| 对比维度 | Session ID | Session Ticket |
|---|---|---|
| 状态存储 | 服务器内存(有上限) | 客户端(票据本身) |
| 负载均衡场景 | 需要会话亲和性(Sticky Session) | 无状态,天然支持 |
| 安全性 | ID 泄露需要绑定服务器 | 票据被加密,但密钥需妥善保管 |
| 恢复速度 | 0-RTT(服务端同步) | 0-RTT(服务端需解密) |
我曾经在生产环境遇到过一个问题:用了 Session ID,但服务器是集群部署,第一次请求落在 A 机器,第二次请求落在 B 机器,结果无法恢复会话,又走了一次完整握手。后来切到 Session Ticket,配合统一的票据加密密钥,彻底解决了。这就是我为什么偏爱 Session Ticket 的原因。
ssl_session_ticket_key 指令。
但会话复用也有缺点。它只能复用连接,不能解决证书验证的开销。每次连接,客户端依然需要验证服务器的证书链。而证书链验证包含 CRL 或 OCSP 查询,这又是一个潜在的延迟点。
2.3 OCSP:在线证书状态查询
证书的有效期很长,但中间可能被吊销。浏览器必须确认证书当前有效。传统的做法是 CRL(证书吊销列表),但列表越来越大,下载太慢。于是有了 OCSP。
OCSP 协议很简单:客户端直接问 CA 的 OCSP 响应器,“这个证书还活着吗?”响应器回答“好”、“坏”或“未知”。
但我得说,传统 OCSP 有一个致命伤——它引入了额外的网络请求。浏览器需要去连接 OCSP 服务器,这又是一个 RTT。如果 OCSP 服务器挂了,浏览器可能还要超时等待,或者采用“软失败”策略(即允许连接,但风险自担)。
更糟糕的是,OCSP 请求的响应可能包含隐私信息——CA 能知道“哪个 IP 在什么时间查询了哪个证书”。所以有些隐私敏感的部署会考虑 OCSP Stapling 来解决。
2.4 OCSP Stapling:把状态塞进握手
OCSP Stapling 的思路很简单:不要客户端去问 CA,而是服务器自己定期去问 CA,拿回一个带时间戳的 OCSP 响应(称为“钉书钉”)。然后在 TLS 握手阶段(Certificate 之后),服务器把这个响应一起发给客户端。
客户端收到后,只需要验证这个响应上的 CA 签名和时效性,就无需再发起额外的网络请求。整个过程在握手内部完成,不增加 RTT。
我举个例子理解它:完整握手像你自己跑到政务大厅办证明,OCSP 是又跑了一趟另一个窗口。OCSP Stapling 则是单位统一给你办好,你直接出示就行。
服务器拿回 OCSP 响应需要缓存,不能每次客户端请求都去 CA 刷新。一般缓存周期是 4 小时到 7 天,由 CA 的 nextUpdate 字段决定。
2.5 本章知识图谱
下面这张图展示了 TLS 完整握手与会话复用、OCSP Stapling 的整体关系。你可以看到优化点主要集中在哪里。
从这张图可以看到,会话复用省掉了握手阶段的 1-2 个 RTT,OCSP Stapling 省掉了证书验证阶段的 1 个额外 RTT。两者叠加起来,效果非常可观。
2.6 为什么必须关注这些基础
我见过不少团队直接跳过基础,上来就调 Nginx 参数。结果调了半天,性能提升有限。为什么?因为你不知道瓶颈到底在哪。
比如你在 Nginx 里开启了 ssl_session_cache,但用的是 Session ID,而后端又是多台服务器且没有配置会话亲和性。那缓存几乎不会被命中,等于白开。
再比如你配置了 OCSP Stapling,但没有处理 CA 的更新策略,导致钉书钉过期,客户端再次回退到完整查询。这种配置错误在真实环境中非常常见。
所以,理解协议层面的基础,是后续配置和调优的前提。你不仅要知道怎么做,还要知道为什么这么做。