大型应用https的实践(一) ——— http升级https

0.本文前提条件

  • 若是所有的资源加载都站内访问,那么 https 实践就比较相对容易。
  • 本文讨论的是大型应用,涉及多资源,跨第三方的一些实践总结整理

1.上 https 的必要性

使用 HTTPS 协议,主要是为了保护用户隐私,防止流量劫持,保证传输内容的安全性。要是只是主域名上了 https,是否就万事大吉了呢?
但是主域名加载的资源,比如 js、css、图片没有上 HTTPS ,会怎么样?
从效果上说,你的 js,css,图片等都会存在被劫持的风险,假设 js 不在主域名下被劫持,这些内容被篡改 / 嗅探了,那么可能影响的就是整个页面的渲染错误或逻辑执行错误。那么主域名上 https 就失去意义了。
https worming

很多用户看见这个链接会习惯性的点“是”,这样非 HTTPS 的资源就被禁止加载了。非 IE 的浏览器很多也会阻止加载一些危害程度较高的非 HTTPS 资源(例如 js)。
我们发现移动端浏览器的限制目前会略松一些。

所以这里要是没做好,很多情况连网站的基本功能都没法正常使用。

2.https 的升级

2.1、个人小站点

只要能买到、或使用有限期的免费的域名证书文件,只需要把主域名替换为 HTTPS 接入就可以了。
然后将所有的静态资源或者请求,从http:// 换成 //即可。

2.2、中级的站点

需要 cdn 或对应的服务供应商都切换到 https,大部分的 cdn 供应商 都是支持 http 与 https 两种,如网宿,七牛,aws 等等。

2.2.1、CDN 使用 HTTPS 常见的方案有:

  1. 网站主提供私钥给 cdn,回源使用 HTTP。
  2. cdn 使用公共域名,公共的证书,这样资源的域名就不能自定义了。回源使用 HTTP.
  3. 仅提供动态加速,cdn 进行 tcp 代理,不缓存内容。
  4. CloudFlare 提供了 Keyless SSL 的服务,能够支持不愿意提供私钥, 不想使用公共的域名和证书却又需要使用 cdn 的站点了。

还使所有自己的第三方服务,都是支持 http 与 https 两种协议。一迁移到自己的 cdn 或者 idc 吧,二强制要求第三方自己能支持 HTTPS 。

2.3、大型的站点

资源只从本站的主域, 主域的子域,或者自建 / 可控的 cdn 域名加载,几乎没有第三方资源。如果网站本身的特性就如此,或愿意改造为这样的类型,部署 HTTPS 就相对容易。Google Twitter 都是非常好的范例。优点:已经改成这样的站点,替换 HTTPS 就比较容易。缺点:如果需要改造,那么要很大的决心,毕竟几乎不能使用多样化的第三方资源了。

那么大型站点使用的 cdn 的供应商往往有一到三家 cdn 服务商,都会建立自身的 cdn 域名,然后动态解析到各家 cdn 服务商,还会根据同步的网络,例如 4g 网络,电信网络,移动网络等不同的分发解析,避免一家 cdn 服务商出现波动,导致无法访问的情况。

部分还需要兼容一些,例如政府,企事单位,或者体量比自己的大,无法强迫升级 https 的内容调用,资源引用等。

2.3.1对于域名的选择

域名对访问速度的影响具有两面性:域名多,域名解析和建立连接的时间就多;域名少,下载并发度又不够。

HTTPS 下重建连接的时间成本比 HTTP 更高,对于上面提到的简单的大型站点, 可以用少量域名就能满足需求,对于例如富展现样式较多的站点来说,页面可能展示的资源种类太多。而不同类型的资源又是由不同的域名 (不同的产品 或者第三方产品) 提供的服务,换一个大的模块就可能需要重新建立一些资源的 ssl 链接,会让用户感受到卡顿。

如果将域名限制在有限的范围,维持和这些域名的连接,合并一些数据,加上有 spdy,http2.0 来保证并发,是可以满足我们的需求的。

2.3.2 链接复用

连接复用率可以分为 tcp 和 ssl 等不同的层面,需要分开进行分析和统计。

浏览器连接数
firfox22
firfox3+6
chrome6
ie86
ie10+8
safiri 56
Opera 126

单个域名的连接数基本上是 6 个。所以只能通过增加域名的方式来增加并发连接数。在 HTTP 场景下,这样的方式没有什么问题。但是在 HTTPS 连接下,由于 TLS 连接建立的成本比较高,增加并发连接数本身就会带来较大的延迟,所以对域名数需要一个谨慎的控制。

特别是 HTTP/2 即将大规模应用,而 HTTP/2 的最大特性就是多路复用,使用多个域名和多个连接无法有效发挥多路复用和压缩的特性。

2.3.3 预建连接

既然从协议角度无法减少握手对速度的影响,那能不能提前建立连接,减少用户可以感知的握手延迟呢?当然是可以的。思路就是预判当前用户的下一个访问 URL,提前建立连接,当用户发起真实请求时,TCP 及 TLS 握手都已经完成,只需要在连接上发送应用层数据即可。

最简单有效的方式就是在主域下对连接进行预建,可以通过请求一些静态资源的方式。但是这样还是不容易做到极致,因为使用哪个连接,并发多少还是浏览器控制的。例如你对 a 域名请求一个图片,浏览器建立了两个连接,再请求一张图片的时候,浏览器很大概率能够复用连接,但是当 a 域名需要加载 10 个图片的时候,浏览器很可能就会新建连接了。

2.3.4 Spdy 的影响

Spdy 对于连接复用率的提升非常有效,因为它能支持连接上的并发请求,所以浏览器会尽量在这个链接上保持复用。

2.3.5 其它

也可以尝试一些其他发方法,让浏览器在访问你的网站之前就建立过 HTTP/2 连接,这样 session 能够复用。HSTS 也能有效的减少跳转时间,可惜对于复杂的网站来说,开启需要考虑清楚很多问题。

2.3.6 优化效果

如果,不开启HSTS,用户直接访问主域名,再通过302跳转 https,增加的时候平均400ms,其中302跳转与ssl握手的因素各占一半。这里面存在一定的优化空间。

3.http升级到https常见问题

3.1.传递 Referrer

我们将自己的站点升级为https,但是一般站点都有外链,让外链都升级https,不现实。很多网站还会根据referrer判断流量的来源。
如果不做任何设置,你会发现在HTTPS站点中点击外链并没有将 referrer 带入到HTTP请求的头部中(http://tools.ietf.org/html/rfc7231#section-5.5.2)

现代的浏览器可以用 meta 标签来传递 refer (http://w3c.github.io/webappsec/specs/referrer-policy)。
传递完整的 url
<meta name=”referrer” content=”always”/>

只传递站点,不包含路径和参数等。
<meta name=”referrer” content=”origin”/>

对于不支持 meta 传递 referrer 的浏览器,例如 IE8, 我们怎么办呢?

可以采用再次跳转的方法,既然 HTTPS 下不能给 HTTP 传递 referer,我们可以先从 HTTPS 访问一个可控的 HTTP 站点,把需要传递的内容放到这个 HTTP 站点的 url 中,然后再跳转到目标地址。

3.2. form 提交

这种情况比较少见,就是form 表单提交第三方http服务,浏览器会不安全的警告。

可以和 referrer 的跳转传递采取相似的逻辑。

但是这样对 referer 和 form 等内容的方案,并不是完美的解决方法,因为这样还是存在不安全的因素(劫持,隐私泄露等 )。
理想情况,推进更多的站点迁移至 HTTPS。

3.3、视频播放

使用 http 的协议来播放视频,那么浏览器仍然会有不安全的提示。所以你有两种选择,1 让视频源提供 HTTPS。2 使用非 HTTP 的协议,如 rtmp 协议。

3.4、用户异常

1、用户的系统时间设置错误,导致提示证书过期。

2、用户使用 fiddler 等代理进行调试,但是没有添加这些软件的根证书,导致提示证书非法。

3、用户使用的 Dns 为公共 dns 或者跨网设置 dns,一些请求被运营商作为跨网流量拦截。

4、连通性有问题,我们发现一个小运营商的 https 失败率奇高,又没法联系到他们,只能不对他们进行 HTTPS 的转换。

5、慢。有时由于网络环境的因素,用户打开其他网站也慢,ping 哪个网站都要 500-2000ms。这时 https 自然也会很慢。

4. 结束语

面对困难和挑战,有充足的动力支持着我们前进,https 上线后,劫持等原因导致的用户功能异常,隐私泄露的反馈大幅减少。

热心的用户经常会向我们反馈遇到的各种问题。在以前,有时即使我们确定了是劫持的问题,能够解决问题的方法也非常有限。每当这种时候,自己总会产生一些无力感。

HTTPS 的全站部署,给我们提供了能解决大部分问题的选项。能让一个做技术的人看到自己的努力解决了用户的问题,这就是最棒的收获。