一个HTTPS难题的排查,商用证书

上周三临下班的时候,收到一个用户不能打开企业邮箱页面的投诉,最终发现是 HTTPS 的问题,这篇文章完整记录了处理过程,解决投诉后,我也在思考问题产生的原因。

客户端程序访问 HTTPS 域名时,提示证书不匹配。

原因:客户端不支持 SNI
当域名经过CDN,或者一个IP上有多个不同证书的 HTTPS 网站时,如果客户端程序不支持 SNI,就会提示证书不匹配的现象。

排查步骤:

  1. Windows 用 IE、Firefox、Chrome 等浏览器打开该 https 网站,显示证书正常。
  2. Linux 用 curl -v https://domain.com | head -1 命令,显示证书正常。
    以上任意一个步骤均可以验证服务器证书部署是否有问题。如果服务器证书没有问题,则问题出在客户端不支持 SNI。

解决办法:

  1. 临时不校验证书,等后续客户端的代码升级之后,再进行校验证书。
  2. 升级 http client 客户端的版本库到最新的稳定版本,同时修改相应的配置。
  3. 域名解析不经过CDN,或是该域名在服务器上作为第一个或是唯一的SSL证书部署。[临时回避触发问题的条件,治标不治本]

收到投诉后,我们的运维同事使用 QQ 远程连接用户桌面(最有效、最快速的问题排查手段)的功能了解具体的情况,初步情况如下:

浏览器访问HTTPS 网站时,静态资源显示有问题, 提示 mix-content

原因:访问流量经过了一个或多个负载均衡,后端代码以为用户访问的还是 HTTP 协议,拼接静态资源的URL仍然以 "http://" 开头。
解决办法: 服务器需要额外配置,某些后端使用旧框架代码需要微调。

nginx 配置

location / {
  proxy_http_version 1.1;
  proxy_set_header Connection "";
  proxy_ignore_client_abort on;
  proxy_pass http://upstream_srv;
  proxy_set_header   Host             $host;
  proxy_set_header   X-Real-IP        $remote_addr;
  proxy_set_header   X-Forwarded-For    $proxy_add_x_forwarded_for;
  proxy_set_header   X-Forwarded-Proto $scheme;
  proxy_set_header   X-Forwarded-Port   $server_port;
}

tomcat 配置

server.xml 文件中,<Host> 节点下面 或是 <Engine> 节点下面 增加这一行:
<Valve className="org.apache.catalina.valves.RemoteIpValve"
             remoteIpHeader="X-Forwarded-For"
             protocolHeader="X-Forwarded-Proto"
             protocolHeaderHttpsValue="https"/>

后端代码框架处理:

判断 HTTP 头 X-Forwarded-Proto 是否存在,若存在并且值为 "https",
则认为此服务是 https 协议。

前端代码处理

  • 佛系前端1
    前端代码不判断协议类型,绝对路径不写 http://domain.com/j.js , 也不写 https://domain.com/j.js , 直接写成自适应的 //domain.com/j.js , 谷歌,推特都是这样做的。
  • 佛系前端2
    当然 url中不写域名,写成相对路径的 /path/file.js 或是 ../path/file.js 也是没有问题的。
  • Chrome 打开企业邮箱官网(
  • 但登录 webmail(

HTTP/2 协议 不支持 WebSocket

截至到目前,2017年11月,HTTP/2 协议 不支持 WebSocket ,如果有业务需要使用WebSocket 协议,记得要关闭 HTTP/2 。

运维同事使用 Chrome 开发者工具发现 webmail 页面(该页面能够正常输出数据)引入的静态元素无法加载,将某一个 js 文件(i0.sinaimg.cn 域名)单独在 Chrome 中打开,页面出现 ERR_SSL_VERSION_OR_CIPHER_MISMATCH 错误。

可能有些同学奇怪,为什么 js 文件打不开就出现空白页面?这和我们的前端开发架构有关,页面的渲染极度依赖 js,如果 js 无法加载整个页面就无法呈现。

看到 ERR_SSL_VERSION_OR_CIPHER_MISMATCH 错误,我第一反应是服务器 https 配置兼容性不好或者客户端https 配置支持不够。考虑到只有个别用户投诉,再加上我本来就知道邮箱 https 配置兼容性非常广(支持 tls 1.0、tls 1.1、tls 1.2),所以重点怀疑客户端 https 的问题。

进一步排查发现用户的 Chrome 版本是 36,潜意识认为是用户浏览器版本过低的问题,做了两个检查:

查看该 Chrome 版本是何时发布

图片 1chrome 36 版本发布时间

通过上图看出 2014 年发布的,也不算太旧。

查看该 Chrome 版本 https 支持的最高版本

通过 SSL Labs User Agent Capabilities 工具 检测结果如下:

[图片上传失败...(image-677f7c-1533520758958)]

通过上图看出该版本最高支持 tls 1.2,不存在 https 版本配置过低的问题。

既然从理论上排除了 Chrome 兼容性问题,我想使用 Chrome 开发者工具菜单查看具体的 https 报错信息,悲催的是 chrome 36 版本居然没有菜单。。。最好的排查工具无法使用了,该版本的开发者工具如下图:

图片 2chrome 36 版本开发者工具没有 Security 菜单

此时我抓瞎了,继续思考,有两个新的现象进入脑子:

webmail 360 浏览器能够正常访问,IE、Chrome 无法访问,其实这一条信息干扰性极大,让我怀疑还是客户端兼容性的问题。

由于不是我远程连接用户桌面,所以当时也没有查看 360 开发者工具的调试信息,这是非常可惜的一点。

Chrome 访问企业邮箱官网没有问题,这其实是非常重要的一条信息,如果是客户端(IE、Chrome)问题,为啥官网 https 访问没有问题,我打开开发者工具看了一下,发现官网引入的 js 元素域名是 www.sinaimg.cn。

问题逐步清晰了,官网和 webmail 引入的 js 元素域名是不一样的,我们公司所有的静态元素都部署在自有 CDN 上(事后才知道也引入了阿里云 CDN),是否这两个域名配置的证书以及 HTTPS 配置不一样?虽然本就知道公司证书都是 SAN 泛域名证书,所有的域名以及子域名都使用同一张证书,但从严谨的角度考虑,我还是使用SSL Labs SSL Server Test 工具测试 https 配置情况。

这个工具会扫描对应域名所有的 IP,然后显示该 IP 下的证书、HTTPS 配置的具体情况,测试 www.sinaimg.cn 结果如下:

图片 3ssllabs analyze www.sinaimg.cn 检测

通过上图可见整个配置检测没有问题。接着测试 i0.sinaimg.cn,结果如下:

图片 4ssllabs analyze i0.sinaimg.cn 检测

出现上图的的原因就是 CDN 的某个点的 https 配置无法获取到,工具中止了检测。

此时问题逐步清晰了,公司静态池CDN 部署了很多点,是否是某个点 https 配置有问题?CDN 由公司专门团队维护,立刻向他们反馈,五分钟后问题解决。

得到的反馈就是静态池也使用了阿里云的 CDN(最近刚加的点,投诉用户正好访问了这个 CDN 点),而这个 CDN 点居然没有配置支持 https。。。

CDN 同事在阿里云开启 443 https 服务(主要工作是上传 i0.sinaimg.cn 证书)后就解决了问题,我们不禁要追问为这个点什么没有 https 部署,他们的解释是没有接到这个点要支持 https 的需求。。。

对于这个理由,要是我还是当年年少轻狂的我,估计要喷他们了(现在也只能心里喷了),静态池服务早就宣称全站支持 HTTPS 了,为啥还有这问题?CDN 配置是开发人员无法也无需知道的,既然全站 HTTPS 了,新增加一个点是不是应该也要支持?怎么能说没有接收到需求呢?我渣浪的甩锅作风还是一如既往。

有些同学不禁要问,这么大的故障,为啥别的产品不受影响呢?原因就在于 i0.sinaimg.cn 这个域名下的服务使用者可能很少,下一阶段我们要尽快将静态元素迁移到 www.sinaimg.cn 域名上。

解决该问题后,我冷静下来思考,为啥 360 浏览器没有问题?同一台机器 DNS 解析难道不是一样的吗?360 连接的 443 服务器难道和 Chrome 连接的 443 服务器不一致?如果不一致,那么 360 浏览器显示正常是可以理解的,如果一致,那就很难解释了。

由于当时没有看到 360 浏览器访问的具体情况,所以我做了一个测试:

登录阿里云 CDN 控制台,默认 443 端口是关闭的,也就是说故障发生的时候,443 肯定没有开启。

由于我没有阿里云 CDN 服务,所以做了个模拟,用自己的服务器测试 打开,443 关闭),看看 360 浏览器是如何运行的

最终使用 360 浏览器访问该网址,不能成功打开,所以这成了一个悬案了。

通过这件事情,得到的一些体会和想法:

  • 排查问题是需要经验的,经验基于技能的掌握程度,冷静的头脑,熟练借助工具。
  • 很多问题看上去很复杂,但最终的原因是如此无厘头,这说明整个技术体系是混乱的,是割裂的。
  • 实际排查顺序并不是本文描述的那样,也走了很多弯路,如此整理是为了让读者更好的了解排查问题的思路。
  • SSL Labs 工具 SSL Server Test 非常好,它是如何检测出一个域名对应的所有 IP 呢?如果有现成解决方案,我打算基于此,写一个简单的小工具,快速诊断出 https 配置情况。

我最近写了一本书《深入浅出HTTPS:从原理到实战》,欢迎去各大电商购买,也欢迎关注我的公众号(yudadanwx,虞大胆的叽叽喳喳)。

本文由365bet体育在线官网发布于网络编程,转载请注明出处:一个HTTPS难题的排查,商用证书

TAG标签:
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。