4. OCSP Stapling 标准化历程:从 RFC 6066 到 RFC 6961

这一节咱们聊聊 OCSP Stapling 是怎么一步步从草稿变成行业标准的。说实话,我在 2015 年刚接触这个特性时,感觉就是「加了根稻草,却扛住了整个性能瓶颈」。标准化的过程,说白了就是一群聪明人在争论「这跟稻草到底该怎么捆」。

TLS 握手的性能瓶颈,很多人第一个想到的是非对称加密的计算开销。但实际碰过大规模服务的都知道,真正要命的反而是证书状态查询——每一次握手都要去 CA 的 OCSP 响应器问「这个证书还能用吗?」。这就是 OCSP Stapling 要解决的核心矛盾。

4.1 问题的起点:初版 TLS 的证书状态查询

早年的 TLS 协议(如 TLS 1.0/1.1)对证书状态的处理非常简单粗暴:客户端自己拉取 CRL(证书吊销列表)或发起 OCSP 请求。但这里有个严重问题——CRL 动辄几 MB,OCSP 请求又多了一轮网络往返。我当时优化一个金融 APP 的接口时,发现握手阶段有 60% 的延迟都耗在了 OCSP 请求上。嗯,这显然不是个可持续的方案。

我记得那时有一个妥协做法:浏览器会缓存 OCSP 响应,但缓存有效期通常只有几分钟。对于高并发场景,这就像用创可贴去堵大坝的裂缝——根本撑不住。

4.2 RFC 6066:TLS 扩展的诞生(2011)

2011 年,IETF 发布了 RFC 6066,正式定义了 TLS 扩展框架。其中有一个扩展叫做 status_request(也就是 OCSP Stapling 的雏形)。

RFC 6066 的核心设计

  • 在 TLS 握手过程中,服务器可以在 Certificate 消息之后,携带一个 CertificateStatus 消息。
  • 这个 CertificateStatus 消息里,装的正是 OCSP 响应数据。
  • 客户端通过 ClientHello 中的 status_request 扩展,告诉服务器「我需要你帮我带 OCSP 响应过来」。

它的基本流程是这样的:

  1. 客户端发起 ClientHello,带上 status_request 扩展。
  2. 服务器看到后,从本地 OCS Response Cache 取出(或实时去 CA 获取)当前证书的 OCSP 响应。
  3. 服务器在发送 Certificate 后紧跟着发送 CertificateStatus 消息。
  4. 客户端拿到 OCSP 响应后验证签名,确认证书未吊销。

这样一来,客户端不再需要单独向 OCSP 响应器发起请求,直接在握手阶段就拿到了状态信息。我把这个流程整理了一下,你看下面这张图会更直观:

我个人的习惯: 在做排查时,我会先用 openssl s_client -status 命令检查一个站点是否开启了 Stapling。如果返回 Y,说明服务端支持;如果返回 N,那就要进一步看配置了。
OCSP Stapling 标准化流程:RFC 6066 与 RFC 6961 对比 OCSP Stapling 标准化演进流程 RFC 6066 (2011) — 初版 1. ClientHello + status_request 扩展 2. Server 获取 OCSP 响应(缓存/实时) 3. Server → Client: CertificateStatus RFC 6961 (2013) — 多证书链扩展 4. Server 携带完整的证书链状态 5. CertificateStatus 包含多个 OCSP 响应 6. 客户端一次性验证整条链 演进 RFC 6066 解决单证书状态查询; RFC 6961 扩展到完整证书链的批量状态验证

4.3 RFC 6066 的局限与踩坑

RFC 6066 虽然解决了大问题,但也有一些「当年没想到」的坑。我曾经在帮一个分布式系统做压测时,发现某个中间证书被吊销了,但服务端只对叶子证书做了 Stapling——结果那个中间证书的吊销状态漏掉了,导致客户端信任链断裂。更麻烦的是,RFC 6066 只定义了单个 OCSP 响应,对于证书链中多个证书的情况,它没法处理。

坑点一: RFC 6066 仅支持叶子证书的 Stapling。如果中间 CA 证书被吊销,客户端不会主动察觉。
坑点二: CertificateStatus 消息是可选的,服务器即使收到了 status_request 扩展,也可以不返回。很多早期实现(比如某些老旧 Nginx 模块)默认就是「忽略客户端请求」。

4.4 RFC 6961:多证书 Stapling(2013)

两年后,IETF 发布了 RFC 6961(全称是 The Transport Layer Security (TLS) Multiple Certificate Status Request Extension)。这次他们学乖了,直接针对 RFC 6066 的不足做了两点关键改进:

  • 支持证书链: 服务器现在可以在 CertificateStatus 消息中携带多个 OCSP 响应,覆盖整个证书链上的所有证书。
  • 新增状态码: 定义了 certificate_status_unknown 状态,让服务器可以诚实地告诉客户端「我查不到这个证书的状态,你自行决定吧」。

你想想看,如果中间证书被吊销了,RFC 6066 下客户端只能看到一个「Certificate 验证通过」,但实际链上的一个环已经断了。RFC 6961 帮我们补上了这个漏洞。

配置小贴士: 在 Nginx 中启用多证书 Stapling 只需要添加一行:

ssl_trusted_certificate /path/to/ca_chain.pem;
ssl_stapling on;
ssl_stapling_verify on;

我建议把完整 CA 链放在一个 PEM 文件中,这样 Nginx 可以自动提取中间证书进行 Stapling。

4.5 两个标准的差异对比

为了方便你快速过目,我把两个标准的核心差异整理成了表:

对比维度RFC 6066RFC 6961
扩展名称status_requeststatus_request_v2
支持的证书范围仅叶子证书完整证书链(叶子 + 中间 CA)
响应数量单个 OCSP 响应多个 OCSP 响应(支持列表)
状态码good / revoked / unknowngood / revoked / unknown / certificate_status_unknown
与旧版兼容—要求实现必须同时支持 RFC 6066
实际部署情况大量开源项目(Nginx/Apache)使用较少见,主流浏览器支持度不一

说到实际部署,我记得 2017 年左右,Chrome 才开始在桌面端全面支持 RFC 6961。但即便如此,很多 CDN 节点仍然只实现了 6066。我曾经遇到过一个场景:客户端明确要求 v2 扩展,但后端 CDN 回了 6066 格式的响应,结果握手失败——修复方案是在 Nginx 上同时开启两种支持。

4.6 扩展的现实意义:Must Staple 与标准化路线图

还有一个值得提的点是 Must Staple 这个扩展。它不是一个单独的标准,而是 RFC 7633(2015)定义的证书扩展。带有 Must Staple 扩展的证书,要求客户端必须拒绝没有 OCSP Stapling 的 TLS 连接。嗯,这其实是个强制性的安全开关。

标准化演进路线(从 6066 到 6961 再到 Must Staple):
  1. RFC 6066: 让 OCSP Stapling 成为可选功能(大部分服务端选择默认关闭)。
  2. RFC 6961: 修复证书链问题,但仍然可选。
  3. Must Staple(RFC 7633): 在证书里嵌入一个扩展,强制要求 Stapling。

我在生产环境部署 Must Staple 证书时,踩过一个坑:如果 CA 的 OCSP 响应器偶尔故障,服务端无法获取响应,但证书又要求 Must Staple——这种情况下客户端会直接拒绝连接。所以我的建议是:上线 Must Staple 之前,先确认你的 OCSP 缓存层足够可靠。

4.7 你该怎么选?

如果是新项目,我建议直接支持 RFC 6961,但也要回退到 6066 做兼容。大多数 Web 服务器(Nginx 1.11+、Apache 2.4+)都原生支持这两种扩展。配置上其实就多了几行的事情。

整理一下本章的要点:

  • RFC 6066 定义了 OCSP Stapling 的基本模型,但仅限单证书。
  • RFC 6961 扩展了证书链支持,并新增状态码。
  • Must Staple 强制要求 Stapling,但需要配合高可用的 OCSP 缓存。
  • 实际部署时最好双栈支持(6066 + 6961)。