egg.js web 安全配置

前端开发工程师 @ 阿里巴巴

作者:ICBU 谦行

写在最前:欢迎来到阿里巴巴 ICBU 交互&端技术前端团队专栏,我们将与你分享优质的前端技术文章,欢迎关注&交流哟!

egg.js 通过 egg-security 插件提供了大量的 web 安全防护

使用

框架的安全插件是默认开启的,如果我们想关闭其中一些安全防范,直接设置该项的 enable 属性为 false 即可。例如关闭 xframe 防范:

config/config.default.js

exports.security = {
  xframe: {
    enable: false,
  },
};
复制代码

security 支持通过 match 和 ignore 配置生效范围,规则和中间件 match、ignore 一致

exports.security = {
  csrf: {
    ignore: '/example',
  },
}
复制代码

XSS

反射型 xss

反射型的 XSS 攻击,主要是由于服务端接收到客户端的不安全输入,在客户端触发执行从而发起 Web 攻击,防范方式主要是过滤用户的输入

内容输出过滤

当网站需要直接输出用户输入的结果时,使用 helper.escape() 过滤特殊字符

const str = '><script>alert("abc") </script><';
console.log(ctx.helper.escape(str));

// => &gt;&lt;script&gt;alert(&quot;abc&quot;) &lt;/script&gt;&lt;
复制代码

js 内容过滤

根据用户输入内容生成 js 脚本内容时候使用 helper.sjs() 过滤

const foo = '"hello"';

// 未使用 sjs
console.log(`var foo = "${foo}";`);
// => var foo = ""hello"";

// 使用 sjs
console.log(`var foo = "${this.helper.sjs(foo)}";`);
// => var foo = "\\x22hello\\x22";
复制代码

helper.sjs() 用于在 JavaScript 中输出变量,会对变量中字符进行 JavaScript encode, 将所有非白名单字符转义为 \x 形式,防止 XSS 攻击,也确保在 js 中输出的正确性

json 内容过滤

在 JavaScript 中输出 json 使用helper.sjson() 做 json encode,遍历 json 中的 key,将 value 的值中所有非白名单字符转义为 \x 形式,防止 XSS 攻击

<script>
  window.locals = {{ helper.sjson(locals) }};
</script>
复制代码

存储型 xss

基于存储的 XSS 攻击,是通过提交带有恶意脚本的内容存储在服务器上,当其他人看到这些内容时发起 Web 攻击。egg.js 提供了 helper.shtml() 方法对字符串进行 XSS 过滤

// 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;
复制代码

CSRF

csrf 主要防范手段是校验服务器生成 csrf token,egg.js 会在 ctx 和 cookie 上生成 cookie 供开发者使用

同步表单

action 上添加 _csrf 参数

<form 
  method="POST" 
  action="/upload?_csrf={{ ctx.csrf | safe }}" 
  enctype="multipart/form-data">
</form>
复制代码

校验参数可以通过配置修改

// config/config.default.js
module.exports = {
  security: {
    csrf: {
      queryName: '_csrf', // 通过 query 传递 CSRF token 的默认字段为 _csrf
      bodyName: '_csrf', // 通过 body 传递 CSRF token 的默认字段为 _csrf
    },
  },
};
复制代码

ajax

egg.js 默认 会把 csrf token 会被设置在 Cookie 中,ajax 请求可以从 cookie 中取到 token,放置到 query、body 或者 header 中发送给服务端

var csrftoken = Cookies.get('csrfToken');

$.ajaxSetup({
  beforeSend: function(xhr, settings) {
    xhr.setRequestHeader('x-csrf-token', csrftoken);
  },
});
复制代码

通过 header 传递 CSRF token 的字段也可以在配置中改变

// config/config.default.js
module.exports = {
  security: {
    csrf: {
      headerName: 'x-csrf-token', // 通过 header 传递 CSRF token 的默认字段为 x-csrf-token
    },
  },
};
复制代码

CORS

cors 是解决跨域的最常用手段,egg.js 通过 egg-cors 插件提供了支持

安装

npm i egg-cors --save
复制代码

启用插件

// config/plugin.js
exports.cors = {
  enable: true,
  package: 'egg-cors',
};
复制代码

配置规则

egg-cors 基于 @koa/cors 开发,支持其所有配置

// config/config.default.js
exports.cors = {
  // {string|Function} origin: '*',
  // {string|Array} allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH'
  // {Boolean|Function(ctx)} credentials `Access-Control-Allow-Credentials`
};
复制代码

熟悉 cors 的同学知道 cors header Access-Control-Allow-Origin 不支持通配符,配置成 * 会让网站安全形同虚设

egg.js 可以通过两种方式设置动态的 Access-Control-Allow-Origin

函数返回值

// config/config.default.js
exports.cors = {
  origin: function ({ req }) {
    const { origin } = req.headers;
    const whiteList = [
      'http://localhost:7001',
      'http://127.0.0.1:7001',
    ];
    if (whiteList.includes(origin)) {
      return origin;
    }
  },
  allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH'
};
复制代码

白名单

通过 egg-security 插件配合,支持配置域名白名单动态设置,也就是内置实现了上面函数返回值的设置方式

// config/config.default.js
exports.security = {
  domainWhiteList: [
    'http://localhost:7001',
    'http://127.0.0.1:7001',
  ],
};
复制代码

cors 生效还需要客户端 withCredentials 设置和服务器配合,可以参考 阮一峰 cors

❤️ 感谢你看到最后~

阿里巴巴国际站(ICBU,Alibaba.com)是全球最大的跨境贸易和服务平台。我们时刻有新的技术挑战,有足够有趣的挑战满足你所有的好奇心和求知欲,有国外知名合作团队(Google & OpenSky)。

如果你想来 ICBU 和我一起开发前端,欢迎发简历到邮箱 shudai.lyy@alibaba-inc.com ,我们将快速响应你的面试安排。:-)

分类:
前端
标签: