原文地址:
Understanding HTTP Strict Transport Security (HSTS) and preloading it into the browser
https://www.troyhunt.com/understanding-http-strict-transport/

最近几周我去度假,在旅行中我做了一个简单的测试:
首先,我打开了chrome开发者工具,并选择网络选项。
然后,我访问americanexpress.com网站,并查看了网络的加载请求:

我发现,如果我在浏览器输入的网址不包含https的话,浏览器首先会发起http请求。服务器进行响应,返回了HTTP 301 Moved Permanently永久跳转,并通过header提供了跳转地址。

我喜欢尝试好玩的事情,我设置了一个菠萝wifi,当访问上面的网站时,网页会被替换成:

图中是一个来自马达加斯加的马蒂人,跳着波尔卡舞,这个图是从一个视频中剪切出来的。如果换成一个黑客呢,那么他很可能会换成一个钓鱼网站并向你获取AMEX证书,由于浏览器的地址栏显示的就是正确的网址,我想会有很多人被骗中招。

那么,接下来让我们再访问我的网站Have I been pwned?(HIBP),这个网站用来监控数据泄露的受影响用户。

看到了么,第一个请求和上面的示例是一样的,但是响应的状态却是HTTP 307 Internal Redirect 内部重定向。这就是说,chrome浏览器不会发http请求,相反浏览器会再尝试https请求,所以就有了下面的第二个请求。

这是安全关键:


chrome拒绝通过不安全的http协议发出第一个请求。

如何实现上面的安全机制呢?我们可以通过第二个请求的响应头找到答案:

这就是Strict-Transport-Security response header,或者叫做HSTS (HTTP Strict Transport Security),HTTP严格传输安全协议。一旦网站返回了这个header头信息,在max-age时间内,浏览器就再也不会发起HTTP请求了,无论我们怎么试图去改变请求。这里注意max-age字段(315......),这一串数字表示的是秒,相当于一年的时间。当规定的时间失效后,浏览器就会再次可以访问非安全请求......直到该网站再次遇到Strict-Transport-Security响应头,浏览器又不支持HTTP访问,以上过程会一直重复进行。我在我的网站上加了“includeSubdomains”属性,所以它适用于*.haveibeenpwned.com所有网站。

但是有一个问题,如果我从没在一个网站得到过HSTS响应头时,该怎么办?那第一个请求肯定是不安全的,还是会发生窃取AMEX证书(上面的例子)的风险。更重要的是,HSTS响应头必须通过HTTPS发送,所以最初的不安全HTTP请求是不会返回安全头的,就算返回了,黑客也可以通过中间人攻击篡改HTTP请求,将它从响应头中删除。

这就是为什么我会在上面的头文件中加入preload属性(参见上图),这个属性意味着我可以将URL提交到HSTS预加载站点:

这个网站很有趣,它是由Chromium维护的,会保护我们使用HTTPS访问。当我们访问一个含有preload的网址时,会向这里提交该域名。换句话说,当浏览器准备发起请求时,即使你从没访问过这个网站,你的浏览器也只能通过HTTPS访问它。这意味着,刚才提到的问题————第一个请求肯定是不安全的,被彻底解决了。

为了将HSTS preload应用到浏览器中,需要满足一些标准。

  • 有一个有效的https证书。
  • 将所有http请求都要重定向到https,即全站只能使用https。
  • 以该域名为后缀的所有域名,都要使用https,特别是www子域名。
  • 在基本域名上,需要增加HSTS头标识:
  • 1. 有效期至少18周(10886400秒)
    2. 必须指定includeSubdomains属性标记。
    3. 必须指定preload属性标记。
    4. 如果我们使用了重定向,则重定向必须有HSTS标头,而不是重定向后的页面。

    写到这里,我们会觉得HSTS的实现机制非常好,但是有一个隐藏的问题需要注意!

    如果我的网站永远都提供https访问,那么我很乐意使用HSTS方案,让网站不存在http安全隐患。但是假如未来,我的网站不再支持https,这将是灾难————不论如何浏览器都不会发起不安全请求。如果使用了preload,那么所有人都会受到影响。就算不使用preload,那些在有效期内的用户,依旧受到影响。所以,这里有个忠告,在使用HSTS之前,确保你的网站永远不再使用HTTP。

    一旦加入preload,就会向chromium提交HSTS,审查结束后,会出现在Chromium的HSTS预载列表中:
    Chromium’s HSTS preload list

    火狐浏览器也有相应的列表:
    nsSTSPreloadList

    还有一些值得注意的地方。

    首先,chromeium和mozilla这两个列表并不完全相同,比如chromeium会看到大量google的域名,但是这些域名并没有出现在mozilla列表中。这是因为,网址可以通过HSTS的preload进行提交,google和mozilla也可以自己手动提交到自己的列表中。

    其次,这有点令人沮丧!为什么? 因为如果我们今天用的是Chromium的列表,那么全世界就有不到三千个网站会自动使用安全的连接,其中很大一部分是Google的。 而这个列表中,我们澳大利亚四大银行就不在这里。 你可以在那里找到yaporn.tv,但我们却找不到那些重要的银行!这让我回想起我的职位————银行安全职位,不得不说,一个瑞士色情网站比一堆数十亿美元的金融机构在执行安全方面做得更好!

    再有就是,我的网站HSTS和preload做的非常标准,并加入到chromeium,但是还处于chrome和firefox的构建中。 在写这篇文章的时候,网站还不在当前版本(版本号43)的Chrome版本中。我们可以通过输入chrome://net-internals/#hsts,来查询和搜索:

    对于没有在浏览器中构建的网站,如果我们之前访问过该网站,则它也会出现在查询中,这是由于前面提到的header响应生效了。 但是,也可以手动删除这些,删除后就会显示“未找到”结果! 但是,正如我们在“删除区域”部分所看到的那样:


    我们无法删除preloaded条目。

    旧版本Internet Explorer兼容性如何?很多开发者都讨厌IE,截至6月9日IE11已经支持HSTS了。也可以访问这个网站查看兼容性:
    https://caniuse.com/#feat=stricttransportsecurity

    当然,Windows 10的微软下一代“Edge”浏览器中也会支持HSTS,所以现在我们可以自信地说,所有主流浏览器都支持HSTS。 如果有人使用不支持HSTS的低版本浏览器也没关系,浏览器会忽略掉这个header的。

    说到微软,如果你生活在ASP.NET世界,那么推荐这篇文章NWebsec on NuGet by André Klingsheim。 这使得网站很容易添加HSTS头以及一堆其他非常整洁的header安全头。 不管你的选择语言如何,它只是一个响应标题,所以你可以用任何你喜欢的方式返回它。

    在有人对此发表意见之前,未来,我还会为我的网站添加HPKP (HTTP Public Key Pinning) ,也叫HTTP公钥密码header头。 这需要提前计划,实施后可以减少很多其它风险,所以我将会再写一篇文章讲解它。

    本篇文章讲述了HSTS和preload, 随着HTTPS在互联网的普及,HSTS功能也将引领安全领域,希望Let's Encrypt这样的举措能够帮助加快这一进程。 对于那些想要进一步保护他们网站资产的人来说,这是个好时机,也将会彻底组织那些钓鱼网站的攻击者们!