# egg-security Security plugin in egg [![NPM version][npm-image]][npm-url] [](https://github.com/eggjs/egg-security/actions/workflows/nodejs.yml) [![Test coverage][codecov-image]][codecov-url] [![Known Vulnerabilities][snyk-image]][snyk-url] [![npm download][download-image]][download-url] [npm-image]: https://img.shields.io/npm/v/egg-security.svg?style=flat-square [npm-url]: https://npmjs.org/package/egg-security [codecov-image]: https://codecov.io/gh/eggjs/egg-security/branch/master/graph/badge.svg [codecov-url]: https://codecov.io/gh/eggjs/egg-security [snyk-image]: https://snyk.io/test/npm/egg-security/badge.svg?style=flat-square [snyk-url]: https://snyk.io/test/npm/egg-security [download-image]: https://img.shields.io/npm/dm/egg-security.svg?style=flat-square [download-url]: https://npmjs.org/package/egg-security Egg's default security plugin, generally no need to configure. ## Install ```bash $ npm i egg-security ``` ## Usage & configuration - `config.default.js` ```js exports.security = { xframe: { value: 'SAMEORIGIN', }, }; ``` ### Disable security precautions If you want to disable some security precautions, set `enable` porperty to 'false' directly. For example, disable xframe defense: ```js exports.security = { xframe: { enable: false, }, }; ``` ### match & ignore If you want to set security config open for a certain path, you can configure `match` option. For example, just open csp when path contains `/example`, you can configure with the following configuration: ```js exports.security = { csp: { match: '/example', // match: /^\/api/, // support regexp // match: ctx => ctx.path.startsWith('/api'), // support function // match: [ ctx => ctx.path.startsWith('/api'), /^\/foo$/, '/bar'], // support Array policy: { //... }, }, }; ``` If you want to set security config disable for a certain path, you can configure `ignore` option. For example, just disable xframe when path contains `/example` while our pages can be embedded in cooperative businesses , you can configure with the following configuration: ```js exports.security = { xframe: { ignore: '/example', // ignore: /^\/api/, // support regexp // ignore: ctx => ctx.path.startsWith('/api'), // support function // ignore: [ ctx => ctx.path.startsWith('/api'), /^\/foo$/, '/bar'], // support Array // ... }, }; ``` __mention:`match` has higher priority than `ignore`__ ### Dynamic configuration for security plugins depend on context There are times when we want to be more flexible to configure security plugins.For example: 1. To decide whether to enable or disable the xframe security header from the context of the request. 2. To decide csp policies from different request urls. Then we can configure `ctx.securityOptions[name] opts` in the custom middleware or controller,then the current request configuration will overrides the default configuration (new configuration will be merged and override the default project configuration, but only take effect in the current request) ```js async ctx => { // if satisfied some condition // change configuration ctx.securityOptions.xframe = { value: 'ALLOW-FROM: https://domain.com', }; // disable configuration ctx.securityOptions.xssProtection = { enable: false, } } ``` Not all security plugins support dynamic configuration, only following plugins list support - csp - hsts - noopen - nosniff - xframe - xssProtection And in ` helper `: - shtml helper is the same way to configure. ```js ctx.securityOptions.shtml = { whiteList: { }, }; ``` #### Mention - Security is a big thing, please pay attention to the risk of changes in the security configuration (especially dynamic changes) - `ctx.securityOptions` the current request configuration will overrides the default configuration, but it does not make a deep copy,so pay attention to configure `csp.policy`, it will not be merged. - If you configure `ctx.securityOptions`,please write unit tests to ensure the code is correct. ## API ### ctx.isSafeDomain(domain) Whether or not the domain is in the whitelist of the configuration. See `ctx.redirect`. Note: [egg-cors](https://github.com/eggjs/egg-cors) module uses this function internally to determine whether or not send back an `Access-Control-Allow-Origin` response header with the value of safe domain. Otherwise, ignore the request with an error, `No 'Access-Control-Allow-Origin' header is present on the requested resource.` ```js exports.security = { domainWhiteList: ['http://localhost:4200'] }; ``` ## Interface restriction ### CSRF __usage__ * `ctx.csrf` getter for CSRF token Generally used when send POST form request. When page rendering, put `ctx.csrf` into form hidden field or query string.(`_csrf` is the key). When submitting the form, please submit with the `_csrf` token parameter. #### Using CSRF when upload by formData browser: ```html
``` #### Using CSRF when request by AJAX CSRF token will also set to cookie by default, and you can send token through header: In jQuery: ```js var csrftoken = Cookies.get('csrftoken'); function csrfSafeMethod(method) { // these HTTP methods do not require CSRF protection return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); } $.ajaxSetup({ beforeSend: function(xhr, settings) { if (!csrfSafeMethod(settings.type) && !this.crossDomain) { xhr.setRequestHeader('x-csrf-token', csrftoken); } }, }); ``` #### Options there are some options that you can customize: ```js exports.security = { csrf: { type: 'ctoken', // can be ctoken, referer, all or any, default to ctoken useSession: false, // if useSession set to true, the secret will keep in session instead of cookie ignoreJSON: false, // skip check JSON requests if ignoreJSON set to true cookieName: 'csrfToken', // csrf token's cookie name sessionName: 'csrfToken', // csrf token's session name headerName: 'x-csrf-token', // request csrf token's name in header bodyName: '_csrf', // request csrf token's name in body queryName: '_csrf', // request csrf token's name in query refererWhiteList: [], // referer white list supportedRequests: [ // supported URL path and method, the package will match URL path regex patterns one by one until path matched. We recommend you set {path: /^\//, methods:['POST','PATCH','DELETE','PUT','CONNECT']} as the last rule in the list, which is also the default config. {path: /^\//, methods:['POST','PATCH','DELETE','PUT','CONNECT']} ], cookieOptions: {}, // csrf token's cookie options }, } ``` `methods` in `supportedRequests` can be empty, which means if you set `supportedRequests: [{path: /.*/, methods:[]}]`, the whole csrf protection will be disabled. #### Rotate CSRF secret Must call `ctx.rotateCsrfSecret()` when user login to ensure each user has independent secret. ### safe redirect * `ctx.redirect(url)` If url is not in the configuration of the white list, the redirect will be prohibited * `ctx.unsafeRedirect(url)` Not Recommended; Security plugin override `ctx.redirect` method,all redirects will be judged by the domain name. If you need to use `ctx.redirect`, you need to do the following configuration in the application configuration file: ```js exports.security = { domainWhiteList:['.domain.com'], // security whitelist, starts with '.' }; ``` If user do not configure `domainWhiteList` or `domainWhiteList` is empty, it will pass all redirects, equal to `ctx.unsafeRedirect(url)`. `domainWhiteList` and `url` are case insensitive. ### jsonp Based on [jsonp-body](https://github.com/node-modules/jsonp-body). Defense: * The longest callback function name limit of 50 characters. * Callback function only allows "[","]","a-zA-Z0123456789_", "$" "." to prevent `xss` or `utf-7` attack. Config: * callback function default name `_callback`. * limit - function name limit, default by 50. ## helper ### .escape() String xss filter, the most secure filtering mechanism. ```js const str = '><'; console.log(ctx.helper.escape(str)); // => ><script>alert("abc") </script>< ``` In nunjucks template, escape by default. ### .surl() url filter. Used for url in html tags (like `