大型网站的 HTTPS 实践(四)——协议层以外的实践

1 前言

网上介绍https的文章并不多,更鲜有分享在大型互联网站点部署https的实践经验,我们在考虑部署https时也有重重的疑惑。
本文为大家介绍百度HTTPS的实践和一些权衡, 希望以此抛砖引玉。
本文最早发表于百度运维部官方博客

2 协议层以外的实践工作

2.1 全站覆盖https的理由

很多刚接触https的会思考,我是不是只要站点的主域名换了https就可以?答案是不行。
https的目的就是保证传输过程的安全,如果只有主域名上了https,但是主域名加载的资源,比如js,css,图片没有上https,会怎么样?
从效果上来说,没有达到保证网站传输过程安全的目的,因为你的js,css,图片仍然有被劫持的可能性,如果这些内容被篡改/嗅探了,那么https的意义就失去了。
浏览器在设计上早就考虑的这样的情况,会有相应的提示。具体的实现依赖浏览器,例如地址栏锁形标记从绿色变为黄色, 阻止这次请求,或者直接弹出非常影响用户体验的提示(主要是IE),用户会感觉厌烦,疑惑和担忧安全性。
技术分享
很多用户看见这个链接会习惯性的点”是”,这样非https的资源就被禁止加载了。非ie的浏览器很多也会阻止加载一些危害程度较高的非https资源(例如js)。我们发现移动端浏览器的限制目前会略松一些。
所以这里要是没做好,很多情况连网站的基本功能都没法正常使用。

2.2 站点的区别

很多人刚接触https的时候,觉得不就是部署证书,让webserver支持https就行了吗。
实际上对于不同的站点来说,https的部署方式和难度有很大的区别。对于一个大型站点来说,让webserver支持https,以及对webserver在https协议特性上做一些优化,在迁移的工作比重上,可能只占到20%-40%。
我们考虑下以下几种情况下,部署https的方案。

2.2.1 简单的个人站点

简单的定义:资源只从本站的主域或者主域的子域名加载。
比如axyz的个人blog,域名是axyzblog.com。加载主域名下的js和图片。
技术分享
这样的站部署https,在已有证书且webserver支持的情况下,只需要把主域名替换为https接入,然后把资源连接修改为https://或者 //。

2.2.2 复杂的个人站点

复杂的定义:资源需要从外部域名加载。
技术分享
这样就比较麻烦了,主域资源容易适配https,在cdn上加载的资源还需要cdn服务商支持https。目前各大cdn的服务商正在逐渐提供https的支持,需要迁移的朋友可以看看自己使用的cdn是否提供了这项能力。一些cdn会对https流量额外收费。
技术分享
Cdn使用https常见的方案有:
1. 网站主提供私钥给cdn,回源使用http。
2. cdn使用公共域名,公共的证书,这样资源的域名就不能自定义了。回源使用http。
3. 仅提供动态加速,cdn进行tcp代理,不缓存内容。
4. CloudFlare 提供了Keyless SSL的服务,能够支持不愿意提供私钥,不想使用公共的域名和证书却又需要使用cdn的站点了。

2.2.3 简单的大型站点

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

2.2.4 复杂,访问速度重要性稍低的大型站点

复杂的定义:从本站的非主域,或者第三方站点的域名有大量的第三方资源需要加载,多出现在一些平台类,或者有复杂内容展现的的网站。
访问速度要求:用户停留时间长或者强需求,用户对访问速度的耐受程度较高。比如门户,视频,在线交易类(比如火车票 机票 商城)网站。
这样的站点,可以努力推动所有相关域名升级为支持https。我们用下图举例说明下这样修改会导致一个网站的链接发生怎样的改变。
技术分享
负责流量接入的团队将可控的接入环境改造为http和https都支持,这样前端工程的工作相对就少一些。大部分时候将链接从http:// 替换为 // 即可. 在主域名是https的情况下,其它资源就能自动从https协议下加载。一些第三方资源怎么办?一般来说只有两种选择,一迁移到自己的cdn或者idc吧,二强制要求第三方自己能支持https。
以全站https接入的facebook举例。第三方厂商想在facebook上线一个游戏。facebook:请提供https接入吧。第三方想:能赚钱啊,还是提供下https接入吧。所以,足够强势,有吸引力,合作方也有提供https的能力的话,这是完全可行的。如果你的平台接入的都是一些个人开发者,而且还赚不到多少钱的情况下,这样就行不通了。
优点:前端改动相对简单,不容易出现https下还有http的资源问题。
缺点:通常这样的实现下,用户的访问速度会变慢,比如从2.5秒变为3秒,如上述的理由,用户还是能接受的。对第三方要求高。

2.2.5 复杂,访问速度有严格要求的大型站点

复杂的定义:同上。
访问速度要求:停留时间不长,用户对访问速度的心理预期较高。
但是如果用户把网站当作工具使用,需要你很快给出响应的时候,这样的实现就不好了。后续几个部分我们介绍下这些优化的抉择。

2.3 域名的选择

域名对访问速度的影响具有两面性:域名多,域名解析和建立连接的时间就多;域名少,下载并发度又不够。
https下重建连接的时间成本比http更高,对于上面提到的简单的大型站点,可以只用1-3个域名就能满足需求,对于百度这样富展现样式较多的搜索引擎来说,页面可能展示的资源种类太多。而不同类型的资源又是由不同的域名(不同的产品 或者第三方产品)提供的服务,换一个词搜索就可能需要重新建立一些资源的ssl链接,会让用户感受到卡顿。
技术分享
如果将域名限制在有限的范围(一般2-6个左右),维持和这些域名的连接,合并一些数据,加上有spdy,http2.0来保证并发,是可以满足我们的需求的。我们的现状是:百度搜索有数百个资源域名在加载各类的资源。这就变成了如何解决这样的问题:如何用2-6个有限的域名提供数百个域名的服务,这就涉及了下一节,代理接入与cdn。

2.4 代理接入

当域名从数百域名缩减到个位数的时候,就不可避免的需要谈到统一接入,流量转发和调度等内容。通常的站点资源大都是从主域名+cdn进行加载,所以我们可以把域名分为这两类,进行替换。
技术分享
替换后的几个cdn域名都指向相同的cname,这样的话意味着用户访问的途径变为如下的方式。
技术分享
这样ssl的握手只在用户和两类节点之间进行,维持连接相对容易很多,也不需要每个域名都去申请证书,部署https接入。
这个方式会遇到域名转换,数据透传,流量调度等一系列的问题,需要进行整体设计架构,对很多细节都需要进行优化,在运维和研发上都有不小的投入。
理想的方式:这样就只需要和cdn节点进行https握手,大幅缩短了握手的rtt时间(cdn节点一般广泛的分布在离用户很近的地方,而主域节点一般都比较有限).这样部署会对cdn的运维和研发能力有更高的要求。
技术分享
大家有没发现,这样的接入就把一个复杂的站点变为简单的站点了?

2.5 连接复用

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

2.5.1 连接复用的意义

HTTP协议(RFC2616)规定一个域名最多不能建立超过2个的TCP连接。但是随着互联网的发展,一张网页的元素越来越多,传输内容越来越大,一个域名2个连接的限制已经远远不能满足现在网页加载速度的需求。
目前已经没有浏览器遵守这个规定,各浏览器针对单域名建立的TCP连接数如下:
技术分享
从上表看出,单个域名的连接数基本上是6个。所以只能通过增加域名的方式来增加并发连接数。在HTTP场景下,这样的方式没有什么问题。但是在HTTPS连接下,由于TLS连接建立的成本比较高,增加并发连接数本身就会带来较大的延迟,所以对域名数需要一个谨慎的控制。
特别是HTTP2即将大规模应用,而HTTP2的最大特性就是多路复用,使用多个域名和多个连接无法有效发挥多路复用和压缩的特性。
那HTTPS协议下,一张网页到底该有多少域名呢?这个其实没有定论,取决于网页需要加载元素个数。

2.5.2 预建连接

既然从协议角度无法减少握手对速度的影响,那能不能提前建立连接,减少用户可以感知的握手延迟呢?当然是可以的。思路就是预判当前用户的下一个访问URL,提前建立连接,当用户发起真实请求时,TCP及TLS握手都已经完成,只需要在连接上发送应用层数据即可。
最简单有效的方式就是在主域下对连接进行预建,可以通过请求一些静态资源的方式。但是这样还是不容易做到极致,因为使用哪个连接,并发多少还是浏览器控制的。例如你对a域名请求一个图片,浏览器建立了两个连接,再请求一张图片的时候,浏览器很大概率能够复用连接,但是当a域名需要加载10个图片的时候,浏览器很可能就会新建连接了。

2.5.3 Spdy的影响

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

2.5.4 其它

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

2.6 优化的效果

从百度的优化经验来看看,如果不开启HSTS,用户在浏览器直接访问主域名,再通过302跳转到HTTPS。增加的时间平均会有400ms+,其中302跳转和ssl握手的因素各占一半。但是对于后续的请求,我们做到了对绝大部分用户几乎无感知。
这400ms+还有很多可以优化的空间,我们会持续优化用户的体验。

3 HTTPS迁移遇到的一些常见问题。

3.1 传递Referrer

我们可以把自己的网站替换为https,但是一般的站点都有外链,要让外链都https目前还不太现实。很多网站需要从referrer中判断流量来源,因此对于搜索引擎这样的网站来说,referer的传递还是比较重要的。如果不做任何设置,你会发现在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传递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 用户异常

在https迁移的过程中,也会有不少热心的用户向我们反馈遇到的各种问题。
常见的有以下的一些情况:
1. 用户的系统时间设置错误,导致提示证书过期。
2. 用户使用fiddler等代理进行调试,但是没有添加这些软件的根证书,导致提示证书非法。
3. 用户使用的Dns为公共dns或者跨网设置dns,一些请求被运营商作为跨网流量拦截。
4. 连通性有问题,我们发现一个小运营商的https失败率奇高,又没法联系到他们,只能不对他们进行https的转换。
5. 慢。有时由于网络环境的因素,用户打开其他网站也慢,ping哪个网站都要500-2000ms。这时https自然也会很慢。

4 结束语

对于复杂的大型网站来说,HTTPS的部署有很多工作要完成。
面对困难和挑战,有充足的动力支持着我们前进:https上线后,劫持等原因导致的用户功能异常,隐私泄露的反馈大幅减少。
热心的用户经常会向我们反馈遇到的各种问题。在以前,有时即使我们确定了是劫持的问题,能够解决问题的方法也非常有限。每当这种时候,自己总会产生一些无力感。
HTTPS的全站部署,给我们提供了能解决大部分问题的选项。能让一个做技术的人看到自己的努力解决了用户的问题,这就是最棒的收获。
HTTPS没有想像中难用和可怕,只是没有经过优化。与大家共勉。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。