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));// => ><script>alert("abc") </script><
js 内容过滤
根据用户输入内容生成 js 脚本内容时候使用 helper.sjs() 过滤
const foo = '"hello"';// 未使用 sjsconsole.log(`var foo = "${foo}";`);// => var foo = ""hello"";// 使用 sjsconsole.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 过滤
// jsconst 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>
CSRF
csrf 主要防范手段是校验服务器生成 csrf token,egg.js 会在 ctx 和 cookie 上生成 cookie 供开发者使用
同步表单
action 上添加 _csrf 参数
<formmethod="POST"action="/upload?_csrf={{ ctx.csrf | safe }}"enctype="multipart/form-data"></form>
校验参数可以通过配置修改
// config/config.default.jsmodule.exports = {security: {csrf: {queryName: '_csrf', // 通过 query 传递 CSRF token 的默认字段为 _csrfbodyName: '_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.jsmodule.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.jsexports.cors = {enable: true,package: 'egg-cors',};
配置规则
egg-cors 基于 @koa/cors 开发,支持其所有配置
// config/config.default.jsexports.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.jsexports.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.jsexports.security = {domainWhiteList: ['http://localhost:7001','http://127.0.0.1:7001',],};
cors 生效还需要客户端
withCredentials设置和服务器配合,可以参考 阮一峰 cors
