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

Built with Hugo
Theme Stack designed by Jimmy