部署 HSTS 提升网站安全性

什么是 HSTS?

HSTS 是 HTTP Strict Transport Security 的缩写,字面意思就是“HTTP 严格传输安全”。在 2012 年 11 月发布了 RFC 文件。其实质,是通过服务器和浏览器配合起来,强制用户使用安全连接来访问服务器。

HSTS 大致原理是,服务器在 SSL 连接的网站请求返回中,带上一个响应头(Response Header)信息,要求浏览器使用强制安全,则在下一次用户访问此域名时,浏览器会自动探测用户是否使用了安全连接,如果没有的话,自动在浏览器端重设了 URL,通过安全连接来访问服务器。避免了用户通过不加密的协议与服务器进行通信。

设计 HSTS 的目的是什么?

每当出现一种安全机制,我们都要想想这背后的目的是什么。其实我也并不知道,接下来直接谈谈我的个人想法好了。目前为止,我们所用到的常见浏览器,Chrome,Firefox,IE,都是没人使用非加密通信的 HTTP 协议访问目标网站的,除非用户手动输入来指定使用的协议是 ,浏览器才会跟服务器进行加密通信。

在这种情况下,很多用户的使用习惯是键入域名来访问目标网站,那么浏览器就会自动选用 HTTP 协议,如果网站提供 HTTPS 服务,服务器一般会返回 302,要求用户重新使用 HTTPS 连接访问网站,以获得加密的连接通信。这种场景无论是对用户而言,还是对网站服务而言,都是自然而然的选择。

在现今的网络环境下,越来越多网站提供了全站 HTTPS 服务,但是,用户的习惯已经养成,并不会因为网站提供了 HTTPS 服务,就会每次输入网址的时候手动输入协议,毕竟用户根本不懂技术。那我们就要问了,如果用户每次都是先访问 HTTP 服务,然后经由服务器的跳转再进入 HTTPS 服务可不可以呢,当然是可以的,但是,这么做是有风险的。如果用户身处一个有嗅探或者过滤的网络,那么用户首次连接网站服务的时候,其会话可能就会被窃取,其身份可能会被恶意伪装。如果,有恶意的攻击者,甚至可能在这个过程中,施行中间人攻击。届时,用户根本不知道自己的一切,已经被人看光了,还以为自己在进行安全的 HTTPS 通信呢。

于是,必须有一种浏览器厂商和服务商共同提供的机制,帮助用户直接选择使用 HTTPS 服务,避免跳转导致的弊端,我们就有了 HSTS,这个协议的作用就是服务器通知客户端,以后访问本服务的时候,一律使用 HTTPS 进行访问。

HSTS 的使用

从 2012 年发布至今,主流的浏览器基本都已经支持了。开启 HSTS 也是非常简单的。只要在 HTTPS 服务的返回的 Response Header 里面添加一条 HSTS 的声明就可以开启这个特性了。如下是一个 Nginx 下的配置范例。其原理只是标准的 HTTP Header 而已,其他的 Web 服务器的配置,就不再赘述了,可以查阅相关文档。

这行配置,要增加到提供 HTTPS 服务的段落里面,浏览器如果在访问一个 HTTPS 的网站的时候,收到了这个响应头信息,会首先校验服务器的证书链,如果都正确无误,会把这个服务器的域名加入到本地一个“已知主机列表”中,这是一个类似哈希索引的东西,以后访问任何 HTTP 的服务的时候,都会查询此索引,一旦发现查到了,就在用户的电脑本地直接产生一个 307 内部跳转(Chrome 的行为),让用户连接到对应的 HTTPS 服务上。

因为,整个机制很大一部分依赖客户端的行为,所以,我们必然面临一个问题,就是下发到客户端的参数是不易维护的。上面展示了这个特性涉及到的三个参数。其中必填的参数是 max-age 这个参数指定了一个 HSTS 主机的生效时间,单位是秒,上面填写的时间 31536000 是一年时间。如果网站的 HTTPS 临时无法访问,收到过 HSTS 的浏览器,也将拒绝访问网站提供的 HTTP 服务,也即,优雅降级的可能性被消除了,所以在使用的时候要做好运维工作和风险评估。设置合理的 max-age 也是很必要的。如果设置得太短,增加了用户暴露在风险中的机会,如果设置得太长,则一定程度提高了网站运维的风险。

第二个参数 includeSubdomains 是可选的参数,主要是声明当前 HSTS 主机域名的所有子域也都是 HTTPS 服务,这个参数的设置,会有一定的风险,可能导致有些网站的子域不可用,如果不能确定网站的所有子域都是 HTTPS 服务,不应该添加这个参数,毕竟有些网站,业务还没有扩张,一开始在根域设定了这种参数后,导致子域都必须 HTTPS 了,那可就麻烦了。例如,一开始你购买了 company.com 的域名开始提供服务,到了后期,发现流量上涨,开始部署 CDN,这时候,你发现,cdn.company.com 这个域名已经被加入用户 HTST 里面的,这就很糟糕了。

第三个参数,说得是预加载特性。从上面所有的行为里,我们可以看到,即使使用了这个策略,用户至少还要有一次机会暴露在非安全的网络之下,虽然被入侵的概率已经很低了,但是对于一个服务来说,单是拦截首次访问服务的新客,也可以给服务方造成巨大损失。能不能彻底杜绝这种可能性呢?于是就有了预加载服务,网站服务首先把自己的域名提交给浏览器的厂商进行认证,添加到 Preload 列表里,这样,早在用户听说你的网站域名之前,浏览器里已经预置了这个域名。这无异一柄双刃利剑,一方面提供了近乎绝对的安全策略,另一方面,极大限制了网站的方案选择。Preload 列表影响范围更广,清除更加麻烦。

如果服务方希望清除掉 HSTS 的话,需要将 max-age 设置为 0,但是,这个尴尬就跟客户端分发是一样的,如果用户在此期间没有访问你的网站,那么 HSTS 的记录是删不掉的,所以说,max-age 才显得可贵,切不能设置得时间太长。

总结

总体来说,使用安全连接协议,对用户来说,对服务提供商来说,都是好事。在现今这个网络环境下,还是尽量使用 HSTS 吧,尽快提升互联网整体环境的安全。这已经是大势所趋,苹果,微信,都在强势推广 HTTPS,而像 Let’s Encrpyt,中国的七牛,腾讯云等,也都提供了免费的一年期 SSL DV 证书给每个用户,所以,放心大胆的开启 HSTS 策略吧。