Featured image of post 浅谈 OCSP Stapling

浅谈 OCSP Stapling

自从把博客系统换成 Wordpress 以后,又是缓存,又是精简,又是各种替换,为了优化网站的速度不可不谓花了一番心思,然而在 iPhone 上,我的网站的打开速度却依然时快时慢。在之前两个月里,我一直认为是服务器的性能问题所致。直到有一天我看到并且为网站部署了 OCSP Stapling。

为什么偏偏在 iPhone 上就打开慢?

你可能已经注意到我使用了「打开」而非「加载」,如果你调用 iPhone 上 Safari 的开发者模式查看一个网页的 Handshake 时间,你就会发现一个不容忽视的 1 秒,至于这一秒都花在了哪里,我们先来看看 OCSP 究竟是什么。

OCSP 的概念

先从 SSL 证书的有效期说起

曾经我有一个困惑,为什么没有永久有效的 SSL 证书?难道是单纯因为商业原因?其实并不是,主要原因是为了安全。每次签发机构(CA)签发 SSL 证书时,CA 都会为这个 SSL 证书生成一个私钥。就像家里任何一把钥匙一样,HTTPS 证书的私钥有丢失、泄露的风险,当网站的私钥丢失时,网站应该向证书 CA 申请将他们的证书加入到证书吊销列表(CRL)里。当用户访问 https 站点时,浏览器会自动向 CA 请求吊销列表,如果用户访问的站点提供的证书在 CRL 里,浏览器就不信任这个证书,因为私钥泄漏后,攻击者可能拥有同样的证书。

所以,如果证书永久有效,随着越来越多的私钥丢失,吊销列表也越来越大(因为只有加进去的,没有剔出去的),这既给 CA 的服务器增加流量压力,也会增加浏览器需要下载的数据量。而一旦有效期只有一年或几年,那么CA就可以将那些已经过期了的证书从 CRL 里剔除,因为反正浏览器也不信任过期证书。这种问题也就随之消失,于是,今天看到的证书,有效期都随着安全等级的提高而加长,但除非自签,没有永久有效的证书。

这和 OCSP 有什么关系?

OCSP 即 Oline Certificate Status Protocol,是用于替代 CRL 的协议,解决了一些 CRL 协议存在的问题,以下信息来译自 Wikipedia:

OCSP 响应通常比典型的 CRL 响应更小,这意味着对于客户端和服务器更小的网络负担。

OCSP 响应中需要解析的数据更少,因此客户端需要的运行库比解析典型的 CRL 响应更少更简单

OCSP 中,服务器可以记录主机在何时验证过特定的证书,由于请求不强制加密,相关信息可能被第三方获取。

简单来说,OCSP 做的就是 CRL 的事——验证这个网站的 https 证书是否处于被吊销的状态。

所以为什么打开慢?

我们搭建个人网站时大都没有很高的预算,例如我的网站原本托管在免费的 Github Pages 上,预算能省则省,更别提动辄几千一年的 SSL 证书了。于是你看到的非商业性质的个人网站大都采用了免费的 Let’s Encrypt 证书。而 Let’s Encrypt 的 OCSP 服务器 ocsp.int-x3.letsencrypt.org 线路非常垃圾,实测中部地区联通 traceroute 以供参考:

Let’s Encrypt OCSP服务器的拉胯线路

可以看到服务器在香港,线路却绕美国。这导致 一个很简单的 OCSP 查询请求需要 1 秒的时间来返回结果。这意味着无论你的服务器性能有多强,网络延迟有多低,只要部署的证书是 Let’s Encrypt,网站在 iPhone 上的加载时间就绝对不会快过一秒。这还不是最坏的情况,因为各种莫名其妙的原因,Let’s Encrypt 的 OCSP 服务器域名会时不时的被 GFW 通过 DNS 污染等方式阻断,这就导致客户端在一部分时间里根本无法查询 OCSP。

但为什么只有 iPhone 会加载慢呢?

OCSP 协议有一个很要命的问题:用 https 的人越来越多,验证证书有效性的需求自然会越来越大,OCSP 服务器难道硬扛访问量?当然不是,不同浏览器都会有不同的 OCSP 验证超时时间,超时就先默认证书有效继续进行访问。但是有些"「注重用户体验」 的浏览器,比如 Chrome,自己在浏览器内部做了个本地列表,通过每次浏览器更新实现列表更新。直接查本地的列表速度就非常快了,当然也不存在什么 CA 的 OCSP 服务器被屏蔽的问题。缺点是并非实时更新。但并非所有浏览器都是所谓「注重用户体验」的浏览器,比如 Safari 就默认开启 OCSP 验证。自然无法避免这种中国特色的问题。Chrome 在全球有超过 70% 的市占率,而受 Chromium 开源项目的影响,国内一众「安全」「极速」浏览器都不存在这种问题,这几乎覆盖了国内桌面端所有用户群体。而苹果要求包括 Chrome 在内的所有 iOS 应用都要使用 Webkit 内核,这直接干死了所有的 iPhone 用户。

怎么解决?

总不可能要求所有 iPhone 用户都自己关掉 OCSP 吧?那么解决方法就是——换证书。怎么可能,要是我真换了证书你也就看不到这篇文章了。虽然换证书,比如 Trust Asia 证书的确可以解决问题。但我不想因为换一个证书而告别 Let’s Encrypt 自动续期和支持泛域名的特性。

终于可以谈到 OCSP Stapling 了,即 OCSP 封装,想要开启 OCSP Stapling 非常简单:首先到亚洲诚信提供的 https 检测服务检测自己的证书链是否完整,若是不完整则修复证书链后将修复好的证书重新部署,完成操作后在 Nginx 配置里加上如下两行:

1
2
ssl_stapling on; 
ssl_stapling_verify on;

然后重启 Nginx 服务,大功告成。由于我自己使用的是 Nginx,Apache 请自行搜索相关方法。

然后,iPhone 用户也能愉快的访问你的网站而无需忍受恼人的 GFW 干扰 Let’s Encrypt 的 OCSP 服务器。

那么效果如何呢?

开启OCSP Stapling前排队时间超过1秒

开启OCSP Stapling后排队时间仅200毫秒

可以看到,开启 OCSP Stapling 后,查询验证结果所需要的时间大幅缩短,反映到网站打开速度上是非常明显的感知。

什么东西这么牛逼?

OCSP 封装,顾名思义,即服务器缓存 OCSP 服务器的验证结果,并且在与客户端 SSL 握手时直接发送缓存结果。

OCSP装订,是TLS证书状态查询扩展,作为在线证书状态协议的替代方法对X.509证书状态进行查询,服务器在TLS握手时发送事先缓存的OCSP响应,用户只要验证该响应的时效性而不用再向数字证书认证机构(CA)发送请求,可以加快握手速度。

我的服务器线路虽然亦非很好,但比 Let’s Encrypt 的弟中弟线路好到不知道哪里去了。开启 OCSP Stapling 后,客户端只需要验证服务器事先缓存的 OCSP 查询结果的有效性,免去了向“神优化”的服务器发请求的痛苦,速度自然会快上一大截,在有 OCSP 强制验证的浏览器上初次打开网页的效果尤为明显,统计数据显示,在开启 OCSP Stapling 后,新访客量明显有所提高,大概是拜 OCSP Stapling 节省的大量握手时间所赐。

为什么不默认开启 OCSP Stapling?

听上去 OCSP Stapling 简直就是完美的万金油优化——每个网站都开启 OCSP Stapling,既提升用户的访问体验,也为 OCSP 服务器减轻负担。那为什么新的 Nginx 配置文件不默认开启 OCSP Stapling 呢?其实这是一个很简单的问题。

  • 不是所有网站都有 SSL 证书
  • OCSP Stapling 并非在所有情况下都能提供更好的访问体验
  • 现存方案中 OCSP 并不是唯一的证书验证方案
  • 引入新功能时通常是需要谨慎的,Nginx 用户群体非常复杂,不同用户不同需求

参考

  1. 为什么https证书要设有有效期?
  2. 找不到了
  3. Wikipedia
  4. 玩个机吧

头图来自:KeyCDN