@58fe/hammer-security 中文文档教程
hammer-security
hammer的安全模块
目录结构
├── config
│ └── index.js
├── lib
│ ├── header
│ │ ├── hsts.js
│ │ ├── methodnoallow.js
│ │ ├── noopen.js
│ │ ├── nosniff.js
│ │ ├── referrerPolicy.js
│ │ ├── xframe.js
│ │ └── xssProtection.js
│ ├── helper
│ │ ├── csrf.js
│ │ ├── escape.js
│ │ ├── index.js
│ │ ├── scli.js
│ │ ├── shtml.js
│ │ ├── sjs.js
│ │ ├── sjson.js
│ │ ├── spath.js
│ │ └── surl.js
│ └── utils.js
└── test
├── lib
│ ├── header
│ │ ├── hsts.test.js
│ │ ├── methodnoallow.test.js
│ │ ├── noopen.test.js
│ │ ├── nosniff.test.js
│ │ ├── referrerPolicy.test.js
│ │ ├── xframe.test.js
│ │ └── xssProtection.test.js
│ ├── helper
│ │ ├── scli.test.js
│ │ ├── sjson.test.js
│ │ ├── shtml.test.js
│ │ ├── sjs.test.js
│ │ ├── spath.test.js
│ │ └── surl.test.js
│ └── utils.test.js
└── utils
└── app.js
Helper
csrf
.surl()
url 过滤。
用于在html标签中中要解析 url 的地方(比如 <a href=""/><img src=""/>
),其他地方不允许使用。
对模板中要输出的变量,加 helper.surl($value)
。
特别需要注意的是在需要解析url的地方,surl 外面一定要加上双引号,否则就会导致XSS漏洞。
不使用 surl
<a href="$value" />
output:
<a href="http://www.domain.com<script>" />
使用 surl
<a href="helper.surl($value)" />
output:
<a href="http://www.domain.com<script>" />
.escape()
对字符串进行 xss 过滤,安全性最高的过滤方式。
const str = '><script>alert("abc") </script><';
console.log(helper.escape(str));
// => ><script>alert("abc") </script><
.sjs()
用于在 js(包括 onload 等 event)中输出变量,会对变量中字符进行 JAVASCRIPT ENCODE, 将所有非白名单字符转义为 \x
spath形式,防止xss攻击,也确保在 js 中输出的正确性。
const foo = '"hello"';
// 未使用 sjs
console.log(`var foo = "${foo}";`);
// => var foo = ""hello"";
// 使用 sjs
console.log(`var foo = "${helper.sjs(foo)}";`);
// => var foo = "\\x22hello\\x22";
.shtml()
将富文本(包含 html 代码的文本)当成变量直接在模版里面输出时,需要用到 shtml 来处理。 使用 shtml 可以输出 html 的 tag,同时执行 xss 的过滤动作,过滤掉非法的脚本。
由于是一个非常复杂的安全处理过程,对服务器处理性能一定影响,如果不是输出 HTML,请勿使用。
简单示例:
// js
const value = `<a href="http://www.domain.com">google</a><script>evilcode…</script>`;
// 模板
<html>
<body>
${helper.shtml(value)}
</body>
</html>
// 输出
'<a href="http://www.domain.com">google</a><script>evilcode…</script>'
shtml 在 xss 模块基础上实现的。
- 默认规则
- 自定义过滤项 http://jsxss.com/zh/options.html
例如只支持 a 标签,且除了 title 其他属性都过滤掉:
const html = '<a href="http://www.domain.com" title="test" onClick="hello()"></a>';
const options = {
whiteList: {
a: [ 'href', 'title' ],
},
};
// html
${helper.shtml(html, options)}
// 输出
'<a href="http://www.domain.com" title="test"></a>'
注意,shtml 使用了严格的白名单机制,除了过滤掉 xss 风险的字符串外, 在 默认规则 外的 tag 和 attr 都会被过滤掉。
例如 html 标签就不在白名单中,
const html = '<html></html>';
const options = {
whiteList: {
a: [ 'href', 'title' ],
},
};
// html
${helper.shtml(html, options)}
// 输出
'<html></html>'
常见的 data-xx
属性由于不在白名单中,所以都会被过滤。
所以,一定要注意 shtml 的适用场景,一般是针对来自用户的富文本输入,切忌滥用,功能既受到限制,又会影响服务端性能。 此类场景一般是论坛、评论系统等,即便是论坛等如果不支持 html 内容输入,也不要使用此 helper,直接使用 escape 即可。
.spath()
如果把输入字符串用作 path 路径,需要使用 spath 进行安全检验。若路径不合法,返回 null。
不合法的路径包括:
- 使用
..
的相对路径 - 使用
/
开头的绝对路径 - 以及以上试图通过 url encode 试图绕过校验的结果字符串
const foo1 = '/usr/local/bin';
const foo2 = '../local/bin';
console.log(helper.spath(foo1));
console.log(helper.spath(foo2));
// => null
// => null
.sjson()
json转义
在js中输出json,若未做转义,易被利用为xss漏洞。提供此宏做json encode,会遍历json中的key,将value的值中,所有非白名单字符转义为\x形式,防止xss攻击。同时保持json结构不变。 若你有模板中输出一个json字符串给js应用的场景,请使用 ${helper.sjson(变量名)}
进行转义。
处理过程较复杂,性能损耗较大,尽量避免使用
实例:
<script>
window.locals = ${helper.sjson(locals)};
</script>
.scli()
远程命令执行漏洞,用户通过浏览器提交执行命令,由于服务器端没有针对执行函数做过滤,导致可以执行命令,通常可导致入侵服务器。
如果用户可控变量为命令中的参数。则直接使用安全包函数过滤。
修复前:
cp.exec("bash /home/admin/xxx/run.sh " + port);
修复后:
cp.exec("bash /home/admin/xxx/run.sh " + helper.scli(port));
如果因为业务需要,需要在参数中天加白名单之外的字符。可以将用户输入按照该字符分割,并使用过滤函数过滤每一段数据。
如果用户可控变量为命令中的命令,则和开发协商修改功能或功能下线。
Web 安全头
hsts(Strict-Transport-Security)
默认开启,如果是 http 站点,需要关闭
- maxAge 默认一年
365 * 24 * 3600
- includeSubdomains 默认 false
使用方法:
app.use(hsts(options))
X-Download-Options:noopen
默认开启,禁用IE下下载框Open按钮,防止 ie 下下载文件默认被打开 xss
使用方法:
app.use(noopen())
X-Content-Type-Options:nosniff
禁用 IE8 自动嗅探 mime 功能。例如:text/plain
却当成 text/html
渲染,特别当本站点服务内容未必可信的时候。
使用方法:
app.use(nosniff())
Referrer-Policy
当用户在浏览器上点击一个链接时,会产生一个 HTTP 请求,用于获取新的页面内容,而在该请求的报头中,会包含一个 Referrer,用以指定该请求是从哪个页面跳转页来的,常被用于分析用户来源等信息。但是也有成为用户的一个不安全因素,比如有些网站直接将 sessionid 或是 token 放在地址栏里传递的,会原样不动地当作 Referrer 报头的内容传递给第三方网站。
所以就有了 Referrer Policy,用于过滤 Referrer 报头内容,目前是一个候选标准,不过已经有部分浏览器支持该标准。
指令值 目前包含了以下几种指令值:
enum ReferrerPolicy {
"",
"no-referrer",
"no-referrer-when-downgrade",
"same-origin",
"origin",
"strict-origin",
"origin-when-cross-origin",
"strict-origin-when-cross-origin",
"unsafe-url"
};
使用方法:
app.use(nosniff(options))
X-Frame-Options
默认 SAMEORIGIN,只允许同域把本页面当作 iframe 嵌入。
- value 默认值
SAMEORIGIN
使用方法:
app.use(xframe(options))
X-XSS-Protection
- close 默认值false,即设置为
1; mode=block
使用方法:
app.use(xssProtection(options))
其他
禁止 trace track 两种类型请求
使用方法:
app.use(notAllow())