HTTP Strict Transport Security (HSTS) - HTTP严格传输安全

1. 由来:启用HTTPS也不能保证足够安全

很多网站对外只开放HTTPS,但用户再访问某个网站的时候,在浏览器中却往往只输入网站域名,例如(example.com),而不是( https://example.com ),不过浏览器依然能正确的使用HTTPS发起请求,这个是由于浏览器在背后的协作。

简单说就是浏览器向server发起了一次HTTP请求,在得到一个重定向响应后,发起一次HTTPS请求并得到最终的响应内容,这看起来是个不错的用户体验。

但在建立起HTTPS连接之前存在一次明文的HTTP请求和重定向,使得攻击者可以以中间人的方式劫持这次请求,从而进行后续的攻击,例如窃听、篡改、跳转钓鱼网站等。

劫持请求并跳转钓鱼网站为例

  1. 浏览器发起一次明文HTTP请求,但实际会被攻击者拦截下来
  2. 攻击者作为代理,把当前请求转发到钓鱼网站
  3. 钓鱼网站返回假冒的网页内容
  4. 攻击者把假冒的网页内容返回给浏览器

在整个过程中,攻击者直接劫持了HTTP请求,并返回内容给浏览器,根本不给浏览器建立HTTPS连接的机会,因此浏览器会误以为真是网站通过HTTP对外提供服务,自然就不会向用户报告当前的连接不安全,于是攻击者可以对请求和响应进行窃听和篡改。

2. 解决方法:使用HSTS

劫持出现在HTTPS连接前的HTTP连接,那么解决这个问题的思路自然就变成了如何避免出现这样的HTTP请求,我们期望的浏览器行为是:用户输入域名(example.com),浏览器直接将其转换为HTTPS请求( https://example.com ),直接略过上述的HTTP请求和重定向,从而使得中间人攻击失效

期望行为

  1. 用户在浏览器输入域名,浏览器得知该域名应该使用HTTPS进行通信
  2. 浏览器直接向网站发起HTTPS请求
  3. 网站返回相应内容

但是浏览器如何知道该域名应该是用HTTPS通信呢?

2.1 HSTS

HSTS全称是HTTP Strict Transport Security, 他是一个Web安全策略机制(web security policy mechanism)

HSTS最为核心的是一个HTTP响应头(HTTP Response Header)。通过这响应头让浏览器得知,在接下来的一段时间内,当前域名只能通过HTTPS进行访问,并且在浏览器发现当前连接不安全的情况下,强行拒绝用户的后续访问要求

HSTS Header的语法:

Strict-Transport-Security: <max-age=>[; includeSubDomains][; preload]

其中

  • max-age 必填参数,秒为单位的数值,代表着HSTS Header的过期时间,通常设置为1年,即31536000秒
  • includeSubDomains 可选参数,如果包含,则意味着当前域名机器子域名均开启HSTS保护
  • preload 可选参数,只有当你申请将自己的域名加入到浏览器内置列表的时候才需要使用到它

让浏览器直接发送HTTPS请求

只需要在响应头中加入

Strict-Transport-Security: max-age=31536000; includeSubDomains

就可以告诉浏览器该域名或子域名在接下来的一年内,对其应强制性使用HTTPS

那么如果有效期过了怎么办? 其实HSTS Header存在于每个响应之中,随着用户和网站的交互,这个有效时间时刻都在刷新,再加上有效期通常被设置为1年,所以只要两次请求间隔小于一年,基本上不会出现风险。

3. 攻击者依然有机可乘

但是还有个问题,HSTS Header存在于响应头中,要拿到响应头还是得发送一次HTTP请求,在第一次访问网站的时候,这时候中间人依然可以将这个HTTP请求劫持下来,继续中间人攻击。

4. 防御到底

针对上面的情况,HSTS也作出了应对, 在浏览器中内置一个列表,只要是在这个列表里的域名,无论何时、何种情况,浏览器都只使用HTTPS发起连接,这个列表由Google Chromium维护,主流的浏览器都在使用