谈谈HSTS超级Cookie

0x00 关于HSTS
浏览器在用户输入网址时,会默认使用http协议,即使该站点已经设置了https。

比如www.example.com已经设置了https,但是如果用户在浏览器中只输入www.example.com,那么其实是默认访问的http://www.example.com,这时候服务器端只能做个302跳转,将请求显式指定到https://www.example.com。但是这存在中间人劫持的风险,大致如下图(盗个图):

即中间人劫持了http流量,然后自己去与服务进行https通信,将请求内容再用http返回给被劫持用户,因此该劫持出现在两种情况下:
(1)用户没有通过准确的方式访问页面,除非输入 https:// ,否则浏览器默认以 http 方式访问
(2)HTTPS 页面的链接中包含 http,这个 http 页面可能被劫持
但是HSTS是一个在浏览器端强制实施https的情况,可以杜绝这个场景。
服务器通过设置一个header来告诉浏览器,所有有这个header的都要在浏览器端强制使用https,比如直接访问www.baidu.com,浏览器会自动跳转至https上去,这一切都是在浏览器端执行的:
这个header就是Strict-Transport-Security:max-age=172800,如果max-age设置为0,则表示关闭HSTS。
0x01 HSTS状态漏洞
去年国外研究人员发现一个问题,这个HSTS的状态无论在隐私窗口还是其他地方的窗口都是可以继承的,即www.xxx.com从正常窗口中设置了HSTS状态,这个状态可以在隐私窗口继承,按说这能有什么安全问题呢?下面分析一下。
0x02 HSTS超级Cookie
HSTS实现的超级Cookie并不是什么新东西了,去年就有了,只是看到社区里呆子大神提到了这个东西,所以做一下介绍吧。
所谓超级cookie,就是一种客户端可以保存数据的手段,但是这种手段显然比cookie更厉害,是可以跨任何域的(这里根本没有域的概念了)。具体是怎么做的呢?
这里准备32个域名,并且两种状态的URL,一种是set hsts状态(注意,HSTS必须在HTTPS下才能设置,因此set操作始终发生在https中),另一种是get获取当前域名下hsts状态,这里以0和1来表示。
比如以下URL:
https://13-hsts-lab.radicalresearch.co.uk/hsts/set/1
该后端代码为:

做了两件事,一个是开启HSTS,另一个是标记当前域名下的HSTS状态,echo 1就是开启状态,这个在之后的回调函数中会用到。
另一种是get操作,这个就可以用http协议了,观察是否307到https了,判断HSTS是否生效。
https://13-hsts-lab.radicalresearch.co.uk/hsts/get
比如我们要记录一个二进制字符串:0101,那么需要四个域名进行数据保存,每个域名设置0或者1.
设置过程如下:
域名1:
域名2:
域名3:
域名4:
当我们需要获取该二进制串数据时,需要写个js循环获取并将它们拼接起来:
域名1:
域名2:
域名3:
域名4:
每次访问一次域名,就会从回调函数里获取到当前域名的HSTS的状态(0或者1),然后写个js将这4个访问结果进行组合,就会还原出我们一开始需要保存的二进制串:0101。
到这里基本就讲清楚HSTS 超级Cookie是咋回事儿了,有两个感受:
(1)这个洞说明各个浏览器厂商在功能繁杂的背景下实现一个新的浏览器标准难免会有问题,因为功能实在太多,有一点疏忽就会造成漏洞。
(2)虽然是超级cookie,但是上面的过程你也看到了,每次只能获取一个bit的数据,实际上是非常低效的,猜测实际应用场景应该不太大。
参考文章