Nginx 下启用 HSTS

What is HSTS

HTTP Strict Transport Security (HSTS) is a web security policy mechanism which helps to protect websites against protocol downgrade attacks and cookie hijacking. It allows web servers to declare that web browsers (or other complying user agents) should only interact with it using secure HTTPS connections,[1] and never via the insecure HTTP protocol. HSTS is an IETF standards track protocol and is specified in RFC 6797.

The HSTS Policy is communicated by the server to the user agent via an HTTPS response header field named “Strict-Transport-Security”.[2] HSTS Policy specifies a period of time during which the user agent should only access the server in a secure fashion.[3]

以上来自于维基百科

大概意思是说 HSTS 是一个 web 安全策略装置,用于保护 web 站点免受协议降级攻击和 cookie 劫持。

具体实现是在用户代理和 https 站点之间通讯时,通过服务器端发出来的 HTTPS response 头信息:“Strict-Transport-Security” 来实现的。

Why

为什么要做 HSTS?当然是为了安全。尤其是天朝这网络环境,无良运营商比比皆是,动辄给你劫持、篡改,为了避免这些无聊无耻人等的捣乱,推荐启用全站 HTTPS 并启用 HSTS。

Howto

鉴于 web server 阵营大多已经都是 Nginx 了,这里也就讲下在 Nginx 中怎么启用 HSTS。

其实很简单:就是用 add_header 在 response 中添加一个“Strict-Transport-Security” 头嘛。说起来简单,但实际做起来还是有些弯弯道道的。

配置文件 inc/HSTS.conf

单独做一个配置文件:inc/HSTS.conf,内容如下:

1
add_header Strict-Transport-Security "max-age=300; includeSubDomains" always;

上面配置大致解释下:

  1. max-age:缓存的时间,单位为妙
  2. includeSubDomains:所有子域名都有效
  3. 还有个参数 preload,这里没设置,这是更狠一点的 HSTS 用法,稍后再讲
  4. always:这其实是 add_header 的参数,意思是所有返回值的头都包含这个头信息(缺省是只有 200 等返回值才会带这个头的)

在合适的位置 include 这个配置文件

然后在虚拟机的配置文件中的合适位置 include 这个配置文件。这个就有学问了,注意以下两点:

  1. 在 HTTPS 的 server 块里 include 这个配置文件
    • 有的文章里说要在 HTTP 的 server 块里也要 include 这个配置文件,我仔细查了下 rfc,确定是不需要的
  2. 如果这个 server 的配置块里的某个 location 中有其他 add_header 语句,那么在这个 location 的配置里也需要 include 这个配置文件
    • 这是因为 nginx 中 add_header 命令的继承特性导致的,一般是会继承的,但是当某个 location 块中有其他 add_header 语句的话,上层的 add_header 指令不会被继承。

进阶话题

前面有提到的参数 preload,的确有个功能叫 preload,大概意思是,有个数据库,其中的网站都是启用 HSTS 的,而一些主流浏览器都内置了对这个库的支持,所以呢,如果有人用这些浏览器访问这些网站,http 协议会直接内部强制转换成 https!

怎样把自己的网站域名加入到这个库里呢?

  1. 要在自己网站上设置 http 到 https 的跳转
  2. 要在自己网站 https 启用 HSTS
    1. 要启用 preload 参数
    2. max-age 要长于一年
    3. 要有 includeSubDomains 参数
  3. 然后提交就好了(https://hstspreload.org)