使用 Helmet 给你的站点加个头盔

2021年1月4日 · 3 years ago

使用 Helmet 给你的站点加个头盔

小型站点我一般直接用 ExpressJS 加个 Helmet 就可以跑了,不过之前没仔细看 helmet 都做了什么,所以简单了解了一下。

helmet 配合 express 的用法十分简单:

const express = require("express");
const helmet = require("helmet");

const app = express();

app.use(helmet());

如果把 helmet() 展开,可以看到它其实做了挺多事情:

// This...
app.use(helmet());

// ...is equivalent to this:
app.use(helmet.contentSecurityPolicy());
app.use(helmet.dnsPrefetchControl());
app.use(helmet.expectCt());
app.use(helmet.frameguard());
app.use(helmet.hidePoweredBy());
app.use(helmet.hsts());
app.use(helmet.ieNoOpen());
app.use(helmet.noSniff());
app.use(helmet.permittedCrossDomainPolicies());
app.use(helmet.referrerPolicy());
app.use(helmet.xssFilter());

Helmet 各项安全防范

  1. CSP(Content-Security-Policy)

    在 HTTP 头里返回给浏览器,支持 CSP 的浏览器就可以开启 XSS 保护,只允许配置的域名或本域名的脚本运行。可自定义 header options,默认为空。阮一峰有一篇博客讲解如何设置 CSP 的可以参考一下

  2. Referrer Policy

    参考 MDN 关于 Refferer Header 的隐私安全文章,原本这个 HTTP 头是为了统计当前页面来源的,但是携带的无用信息太多,反而可能导致用户信息泄露。比如说邮件退订这种页面,一般是不需要登录就可以操作,如果这个页面里又有其他外部链接可以跳出去,带上了当前页面的链接地址,则另一个页面就可以轻易拿到这个可操作链接,直接让用户退订邮件。

    helmet 默认返回 no-referrer

  3. Expect-CT

    CT 是 Certificate Transparency 的缩写。今时今日,多数网站都已经配置了 https 保证网络流量加密,大家用的证书都是各大 CA 颁发的。但是实践证明, CA 也有可能干坏事,或者CA证书也有可能被偷了之类的,这样其他人拿到这个证书,再做一个恶意网站钓鱼劫持一下就可以为所欲为了。

    所以 Google 就发起了 CT 这个项目,把所有颁发的证书记录到 Log Server。服务器可以在 HTTP 回包头里告知浏览器是否需要验证一下 CT,当然只对 https 请求生效。

    如果你设置了 reportUri,则浏览器当 CT 验证失败之后,会通过填入的 url 上报给你的服务器。

  4. Strict-Transport-Security

    强制使用 HTTPS,这没什么好说的,如果是新服务,也没什么旧的客户端依赖的话,开启 HTTPS 肯定是更安全的。

  5. X-Content-Type-Options: nosniff

    这其实是针对浏览器(主要是 IE) MIME Sniffing 特性的一个对抗。比如说 IE 会忽略服务器返回 MIME 类型,自行猜测推断出正确的 MIME 然后执行。因为历史原因,MIME Sniffing 特性允许浏览器嗅探包括 application/javascripttext/javascript 等多种类型。这个特性在服务器返回错误 MIME 的时候当然很有用,但是也会引发安全问题。

    比如有个服务器它专门 host 图片资源的,然后有恶意用户上传了一张精心设计的图片(其实是一堆 JS 代码),然后把链接发给用户诱导用户打开它。这时候浏览器如果支持 MIME Sniffing 就可能下载完自动探测为 JS 类型然后执行了。

    现代浏览器基本都支持 X-Content-Type-Options: nosniff,helmet 默认会打开。

  6. X-DNS-Prefetch-Control

    这个 HTTP 头可以告知浏览器是否要提前做 DNS 域名解析,以便在用户点击页面链接的时候省去 DNS 这一步的时间,提升浏览体验。

    网页端可以加一个 <meta><link> 标签来开启这个功能:

    <meta http-equiv="x-dns-prefetch-control" content="on">
    
    <link rel="dns-prefetch" href="//xxx.github.io">
    

    helmet 默认把这个功能关了,理由是保护用户隐私。我觉得 HTTP 头关掉挺合理的,需要的话可以在网页端的首页加上相关域名的预解析标签来解决。

  7. X-Download-Options: noopen

    这是针对 IE 8 一个特性而设置的。IE 8 支持在当前页面下载一个附件后直接打开,这个“打开”如果是一个 HTML 文件,它里面的脚本就可以盗取当前页面的所有信息。

    比如上图的例子,这个 HTML 文件被打开了之后就可以轻松盗取当前页面的 cookie。解决这个问题的方法就是设置 X-Download-Options: noopen

  8. X-Frame-Options: deny

    这个设置项是为了防止 ClickJacking 攻击,这种攻击手段就是在你的页面上覆盖一个透明的 iframe,然后诱使用户去点你的页面。这时候用户就可能一不小心点到了这个 iframe 从而执行了他的恶意代码。

    helmet 默认返回 X-Frame-Options: deny,这样浏览器就不会响应 iframe 的点击事件了。需要的话也可以改成 sameorigin,当前域名。

  9. X-Permitted-Cross-Domain-Policies

    这个是针对 Adobe 产品(Flash/Acrobat)的跨域策略,默认不允许: none。鉴于 Flash 已经凉凉了,改为 none 应该没什么问题。

  10. 删掉 X-Powered-By

    严格来讲这并不是安全原因,只是当你用 PHP/ASP.net/Express 的时候,默认 HTTP 头会填上这个属性,有点广告的意思。删掉这个省点流量(并没有什么流量)。

  11. X-XSS-Protection

    这是把浏览器自己的 XSS Filter 给 disable 掉,因为实在太 buggy XDDD

What's Next

看完了一圈其实 Helmet 也没做特别复杂的事情,源码也很简单,大家可以参考 github 仓库。每个小点就是一个中间件,接受 req/res,然后自行处理一下再传给下一层。

上述默认配置看起来也是比较简单的通用配置,但作为一个小白,如果不细看 Helmet 的配置我对这些“基本安全知识”也是知之甚少,还是颇有收获的。

参考资料