如果把证书链配置完全,还要注意证书链的大小。有一些网站的完整证书异乎寻常地大,达到若干 kb 甚至几十 kb,也就是说,在建立连接之前,就必须先传输这么多的数据。如果做单纯的 PC 网页浏览,或许不会有问题。但对于移动端和程序调用来说,这就是一场灾难。最好的办法,是用 OpenSSL 配合 WireShark 之类的工具,自己动手来测试。如果你熟悉 TCP 相关的知识,往往可以得到更好的优化方案。
OCSP 和 CRL 也不可忽略。这两项技术用来保证撤回证书(让证书失效)的有效性。如果你仔细观察,会发现证书里都指定了对应的 OCSP 或者 CRL 的 URL,用来检查证书是否失效。按道理说,在每次建立 HTTPs 连接时,都应当进行 OCSP 或 CRL 检查。返回结果通常在 1k 左右,如果请求非常频繁,这个因素也应当考虑。
SNI
大家都熟悉“虚拟主机”的概念,它可以让多个域名对应到同一个 IP,让同一台服务器服务多个站点。在 HTTP 时代,可以在 header 中通过 host 来指定需要访问的域名,一切看起来都那么完美。
但是在 HTTPs 时代,却没有这样的好事,传统的 HTTPs 服务很难让多个域名对应到同一个 IP。在进行到 HTTP 通讯之前,必须先建立验证证书建立连接。如果一个 IP 上绑定了多个域名,这个阶段服务器根本没法知道请求对应的是哪个域名,无疑会造成极大的不便。
为了解决这种问题,SNI (Server Name Identification)应运而生了。这种技术说起来复杂,大家可以把它简单理解为“建立 SSL/TLS 通讯时的 host header”,这样就解决了一个 IP 只能配单张证书的问题。
然而 SNI 的诞生历史并不长,许多客户端的支持都存在奇怪的问题。比如 JDK7 支持 SNI,但是 JDK8 的支持又有 bug。而且这种支持往往需要调用原生的 API 才可以实现,Resteasy 之类的类库并不支持。如果对 SNI 的支持有问题,即便配置正确也可能无法建立连接,因为服务端并不能识别此请求需要的证书。
CDN
CDN 已经是业界流行的技术了,对稍微大一点的网站来说,没有 CDN 几乎是不可想象的。HTTP 时代的 CDN 方案相当成熟,HTTPs 的情况则不是如此。
要使用 HTTPs 的 CDN 服务,就要决定是否将证书交给 CDN 提供商。基于中国目前的商业信誉水平,把证书交给 CDN 提供商的风险不可步考虑。恶意揣测,别有用心的人一旦拿到证书,可以很方便地通过 DNS 劫持发起中间人攻击,而完全不被感知到。
另外,HTTP 时代大家都喜欢将资源分散到多个不同的域名,因为建立连接的成本很低,而同一个域名的并发连接数有限。在 HTTPs 环境下,每次建立连接的成本高了很多,频繁下载和验证证书对移动设备和移动网络影响很大(尤其是证书很大的情况)。如果域名非常分散,影响就更加显著。所以在 HTTPs 时代,把域名收缩集中反而是更好的办法。
内容及其它
因为 HTTPs 的内容中无法引用 HTTP 的资源,所以应当保证网页中资源文件的链接都是 HTTPs 的。遗留的许多系统很可能并不注意这些事情,资源都采用绝对地址的形式,这样改起来工作量很大。如果要修改,最好一步到位,直接改成“协议相对 URL”。
绝对地址 URL:
协议相对 URL://www.a.com/b.css
这样浏览器就可以根据当前的协议,自动生成资源的绝对地址,无论是 HTTP 还是 HTTPs,都可以自由切换。
如果资源都是自有的,切换 HTTPs 就相对容易。如果存在外部,尤其是 UGC 的资源,切换到 HTTPs 就很麻烦。如果是超链接,通常采用专门的跳转服务,也就是下面这样:
https://link.my.com/target=www.you.com
如果是图片这类资源,可以设定专门的程序将其抓取过来存放在自己的服务器上,再将地址替换掉即可。但是,这样做很可能要自己承担流量压力,同时还要承担恶意程序攻击的风险。
如果是视频、游戏等富文本、交互复杂的资源,如果源站没有提供 HTTPs 的服务,多半只能忍痛割爱,放弃内嵌展现的形式了。
最后再补充两点经验:
如果真的决定上 HTTPs,最好有个人把 OpenSSL 玩熟,否则很多问题会让你摸不着头脑,OpenSSL 是很好的调试工具,很方便定位问题。