为Hexo网站域名启用HSTS (HTTP严格传输安全)

0x00 什么是HSTS?

一个网站接受一个HTTP的请求,然后跳转到HTTPS,用户可能在开始跳转前,通过没有加密的方式和服务器对话,比如,用户输入 http://foo.com 或者直接 foo.com。这样存在中间人攻击潜在威胁,跳转过程可能被恶意网站利用来直接接触用户信息,而不是原来的加密信息。网站通过HTTP Strict Transport Security通知浏览器,这个网站禁止使用HTTP方式加载,浏览器应该自动把所有尝试使用HTTP的请求自动替换为HTTPS请求。

网站第一次通过HTTPS请求,服务器响应Strict-Transport-Security头,浏览器记录下这些信息,然后后面尝试访问这个网站的请求都会自动把HTTP替换为HTTPS。当HSTS头设置的过期时间到了,后面通过HTTP的访问恢复到正常模式,不会再自动跳转到HTTPS。而之后每次用HTTPS访问,都会更新这个网站的过期时间。

谷歌维护着一个 HSTS 预加载服务。成功提交你的域名后,浏览器将会永不使用非安全的方式连接到你的域名。虽然该服务是由谷歌提供的,但所有浏览器都有使用这份列表的意向(或者已经在用了)。但是,这不是 HSTS 标准的一部分,也不该被当作正式的内容。

详情可以看https://tools.ietf.org/html/rfc6797

0x01 给Hexo添加HSTS

我的Hexo是部署在Netlify + 国内腾讯云CDN分流的,看了一下相关文档,GitHub Pages暂时不支持自定义HTTP Headers或者直接开启HSTS,所以这里不介绍GitHub Pages。其他的Pages服务只要支持自定义Headers也可以实现。

Netlify x Hexo

按照Netlify的文档,要为静态站点开启自定义Headers,只需要在部署的文件夹根目录下添加一个_headers文件,但是Hexo生成html的时候会自动跳过_开头的文件,所以需要用一点特殊的方法。

我们可以利用Hexo API里面的生成器(Generator)来进行文件复制,这样hexo g的时候就可以复制它到/public下面,进而进入部署目录。再查一下发现Hexo默认会加载主题目录/scripts下的所有js文件,因此我们可以在这里新建一个netlify-headers.js文件,例如hexo/themes/material/scripts/netlify-headers.js,然后写入以下内容:

hexo.extend.generator.register('netlify-headers', function(locals){
  var fs = require('hexo-fs');
  var pathFn = require('path');
  var data = fs.readFileSync( pathFn.join( process.env.PWD || process.cwd() , '_headers'));
  return {
    path: "_headers",
    data: data
  };
});

接着,我们在hexo主目录下面新建一个_headers文件,并将HSTS所需要的头写进去:

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload

这里max-age就是前面提到的过期时间,63072000是两年;includeSubDomains表示告诉浏览器,我这个网站的所有子域名也要遵守HSTS,即访问HTTP会跳转到HTTPS;preload不是标准的一部分,只是之后提交到Chromium项目的HSTS预加载列表时会检查,一旦你的域名加入到了这个列表,那么无论用户是否之前访问过,浏览器都会尝试跳转到HTTPS。

接着只要部署就OK了。

腾讯云CDN

这个更简单,配置好之后直接进入后台,选择你想设置的域名-管理-高级配置,找到“HTTP Header配置”,添加HTTP Header,像这样设置即可:

腾讯云CDN配置

提交HSTS Preload List

https://hstspreload.org/ 在这里检查你的域名是否符合要求(即max-age大于一年,有includeSubDomainpreload,检查通过即可提交到HSTS Preload,理论上过几个月就会硬编码到浏览器里。

不过在提交之前建议先看以下下面的说明,因为这个添加之后很难移除

0x02 踩到的坑

Netlify会默认跳转根域名到www,而HSTS Preload那里会检查根域名,但是,Netlify并没有给根域名的跳转加上includeSubDomainpreload因此无法通过,我就采用了折中一点的办法,把根域名解析到我的VPS上面,然后在那上面用Caddy(或者Apach、Nginx之类的都可)做重定向,Caddyfile配置如下:

lonelyion.com {
    #https://caddyserver.com/v1/docs/tls 这里最好用已经签好的证书,直接写tls的话Let's Encrtpy验证过不了
    tls cert key
    header / {
        Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
    }
    redir https://www.lonelyion.com{uri}
}

这样就可以通过HSTS Preload的检查了,成功提交。

0x03 其他站点

其他开启的思路也是一样的,只需要在Header加上Strict-Transport-Security字段即可。

开启HSTS之后,其他所有的子域名也要全上HTTPS,所以在开启之前,请务必确保所有的子域名都可以用HTTPS访问

HSTS Preload Check

HSTS Preload Submit

本页面的全部内容在 CC BY-NC-SA 4.0 协议之条款下提供,附加条款亦可能应用
本文链接:https://www.lonelyion.com/2020/enable-hsts-for-hexo-site/