@58fe/hammer-security

security plugin in hammer frameworks

Usage no npm install needed!

<script type="module">
  import 58feHammerSecurity from 'https://cdn.skypack.dev/@58fe/hammer-security';
</script>

README

hammer-security

Build Status codecov

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

接入csrf 模块, 具体使用方法请看github

.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&lt;script&gt;" />

.escape()

对字符串进行 xss 过滤,安全性最高的过滤方式。

const str = '><script>alert("abc") </script><';
console.log(helper.escape(str));
// => &gt;&lt;script&gt;alert(&quot;abc&quot;) &lt;/script&gt;&lt;

.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>&lt;script&gt;evilcode…&lt;/script&gt;'

shtml 在 xss 模块基础上实现的。

例如只支持 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)}

// 输出
'&lt;html&gt;&lt;/html&gt;'

常见的 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())
    

License

MIT