From ae37c8f8e55c050ec6747a196f77aec197958e02 Mon Sep 17 00:00:00 2001 From: hongzzz Date: Wed, 3 Jul 2024 20:59:24 +0800 Subject: [PATCH] feat: add rotateWhenInvalid option for CSRF token (#98) closes https://github.com/eggjs/egg-security/issues/97 ## Summary by CodeRabbit - **New Features** - Introduced a new security option `rotateWhenInvalid` to control CSRF token rotation when invalid, enhancing overall security. - **Documentation** - Updated `README.md` and `README.zh-CN.md` to include the new `rotateWhenInvalid` configuration option. - **Tests** - Added test cases to verify CSRF token rotation behavior when `rotateWhenInvalid` is enabled. --- README.md | 1 + README.zh-CN.md | 1 + app/extend/context.js | 4 ++++ config/config.default.js | 1 + test/csrf.test.js | 11 +++++++++++ 5 files changed, 18 insertions(+) diff --git a/README.md b/README.md index 9c63127..75a29ef 100644 --- a/README.md +++ b/README.md @@ -214,6 +214,7 @@ exports.security = { 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 + rotateWhenInvalid: false, // rotate csrf secret when csrf token invalid. For multi applications which be deployed on the same domain, as tokens from one application may impact others. 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']} diff --git a/README.zh-CN.md b/README.zh-CN.md index 6f88144..7288f5e 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -143,6 +143,7 @@ exports.security = { headerName: 'x-csrf-token', // csrf token 在 header 中的名称 bodyName: '_csrf', // csrf token 在 body 中的名称 queryName: '_csrf', // csrf token 在 query 中的名称 + rotateWhenInvalid: false, // csrf invalid 时刷新 token,用于同域名下多个业务 token 可能互相影响的情况 refererWhiteList: [], // referer 白名单 supportedRequests: [ // 支持的 url path pattern 和方法,根据配置名单由上至下匹配 url path 正则,建议在自定义时配置 {path: /^\//, methods:['POST','PATCH','DELETE','PUT','CONNECT']} 为兜底规则 {path: /^\//, methods:['POST','PATCH','DELETE','PUT','CONNECT']}, diff --git a/app/extend/context.js b/app/extend/context.js index 4c8e255..6086c44 100644 --- a/app/extend/context.js +++ b/app/extend/context.js @@ -200,6 +200,10 @@ module.exports = { if (token !== this[CSRF_SECRET] && !tokens.verify(this[CSRF_SECRET], token)) { debug('verify secret and token error'); this[LOG_CSRF_NOTICE]('invalid csrf token'); + const { rotateWhenInvalid } = this.app.config.security.csrf; + if (rotateWhenInvalid) { + this.rotateCsrfSecret(); + } return 'invalid csrf token'; } }, diff --git a/config/config.default.js b/config/config.default.js index 41938f0..508d01a 100644 --- a/config/config.default.js +++ b/config/config.default.js @@ -42,6 +42,7 @@ module.exports = () => { headerName: 'x-csrf-token', bodyName: '_csrf', queryName: '_csrf', + rotateWhenInvalid: false, supportedRequests: [ { path: /^\//, methods: [ 'POST', 'PATCH', 'DELETE', 'PUT', 'CONNECT' ] }, ], diff --git a/test/csrf.test.js b/test/csrf.test.js index d013d45..9c3a678 100644 --- a/test/csrf.test.js +++ b/test/csrf.test.js @@ -283,6 +283,17 @@ describe('test/csrf.test.js', () => { }); }); + it('token should be rotated when enable rotateWhenInvalid', async () => { + mm(app.config.security.csrf, 'rotateWhenInvalid', true); + await app.httpRequest() + .post('/update') + .set('x-csrf-token', '2') + .set('cookie', 'csrfToken=1') + .send({ title: 'invalid token' }) + .expect(403) + .expect(res => assert(!!res.header['set-cookie'])); + }); + it('should show deprecate message if ignoreJSON = true', async () => { const app = mm.app({ baseDir: 'apps/csrf-ignorejson' }); await app.ready();