小型站点我一般直接用 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 各项安全防范
- CSP(Content-Security-Policy)
在 HTTP 头里返回给浏览器,支持 CSP 的浏览器就可以开启 XSS 保护,只允许配置的域名或本域名的脚本运行。可自定义 header options,默认为空。阮一峰有一篇博客讲解如何设置 CSP 的可以参考一下。
-
Referrer Policy
参考 MDN 关于 Refferer Header 的隐私安全文章,原本这个 HTTP 头是为了统计当前页面来源的,但是携带的无用信息太多,反而可能导致用户信息泄露。比如说邮件退订这种页面,一般是不需要登录就可以操作,如果这个页面里又有其他外部链接可以跳出去,带上了当前页面的链接地址,则另一个页面就可以轻易拿到这个可操作链接,直接让用户退订邮件。
helmet 默认返回
no-referrer
。 -
Expect-CT
CT 是 Certificate Transparency 的缩写。今时今日,多数网站都已经配置了 https 保证网络流量加密,大家用的证书都是各大 CA 颁发的。但是实践证明, CA 也有可能干坏事,或者CA证书也有可能被偷了之类的,这样其他人拿到这个证书,再做一个恶意网站钓鱼劫持一下就可以为所欲为了。
所以 Google 就发起了 CT 这个项目,把所有颁发的证书记录到 Log Server。服务器可以在 HTTP 回包头里告知浏览器是否需要验证一下 CT,当然只对 https 请求生效。
如果你设置了
reportUri
,则浏览器当 CT 验证失败之后,会通过填入的 url 上报给你的服务器。 -
Strict-Transport-Security
强制使用 HTTPS,这没什么好说的,如果是新服务,也没什么旧的客户端依赖的话,开启 HTTPS 肯定是更安全的。
-
X-Content-Type-Options: nosniff
这其实是针对浏览器(主要是 IE) MIME Sniffing 特性的一个对抗。比如说 IE 会忽略服务器返回 MIME 类型,自行猜测推断出正确的 MIME 然后执行。因为历史原因,MIME Sniffing 特性允许浏览器嗅探包括
application/javascript
,text/javascript
等多种类型。这个特性在服务器返回错误 MIME 的时候当然很有用,但是也会引发安全问题。比如有个服务器它专门 host 图片资源的,然后有恶意用户上传了一张精心设计的图片(其实是一堆 JS 代码),然后把链接发给用户诱导用户打开它。这时候浏览器如果支持 MIME Sniffing 就可能下载完自动探测为 JS 类型然后执行了。
现代浏览器基本都支持
X-Content-Type-Options: nosniff
,helmet 默认会打开。 -
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 头关掉挺合理的,需要的话可以在网页端的首页加上相关域名的预解析标签来解决。
-
X-Download-Options: noopen
这是针对 IE 8 一个特性而设置的。IE 8 支持在当前页面下载一个附件后直接打开,这个“打开”如果是一个 HTML 文件,它里面的脚本就可以盗取当前页面的所有信息。
比如上图的例子,这个 HTML 文件被打开了之后就可以轻松盗取当前页面的 cookie。解决这个问题的方法就是设置
X-Download-Options: noopen
。 -
X-Frame-Options: deny
这个设置项是为了防止 ClickJacking 攻击,这种攻击手段就是在你的页面上覆盖一个透明的 iframe,然后诱使用户去点你的页面。这时候用户就可能一不小心点到了这个 iframe 从而执行了他的恶意代码。
helmet 默认返回
X-Frame-Options: deny
,这样浏览器就不会响应 iframe 的点击事件了。需要的话也可以改成sameorigin
,当前域名。 -
X-Permitted-Cross-Domain-Policies
这个是针对 Adobe 产品(Flash/Acrobat)的跨域策略,默认不允许:
none
。鉴于 Flash 已经凉凉了,改为none
应该没什么问题。 -
删掉 X-Powered-By
严格来讲这并不是安全原因,只是当你用 PHP/ASP.net/Express 的时候,默认 HTTP 头会填上这个属性,有点广告的意思。删掉这个省点流量(并没有什么流量)。
-
X-XSS-Protection
这是把浏览器自己的 XSS Filter 给 disable 掉,因为实在太 buggy XDDD
What's Next
看完了一圈其实 Helmet 也没做特别复杂的事情,源码也很简单,大家可以参考 github 仓库。每个小点就是一个中间件,接受 req
/res
,然后自行处理一下再传给下一层。
上述默认配置看起来也是比较简单的通用配置,但作为一个小白,如果不细看 Helmet 的配置我对这些“基本安全知识”也是知之甚少,还是颇有收获的。
参考资料
- Content Security Policy 入门教程 - 阮一峰的网络日志
- Referer header: privacy and security concerns - Web security | MDN
- Certificate Transparency——证书数据解析 - 知乎
- RFC 6962 - Certificate Transparency
- HTTPS encryption on the web – Google Transparency Report
- Certificate Transparency
- MIME 类型 - HTTP | MDN
- MIME Sniffing Standard
- Cyber-Security:Web应用安全:攻击、防护与检测 | Ribose Yim's Home