Skip to content

Commit

Permalink
PP-13360 csrf express middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
nlsteers committed Dec 16, 2024
1 parent be589b5 commit 74ecebb
Show file tree
Hide file tree
Showing 6 changed files with 375 additions and 2 deletions.
11 changes: 10 additions & 1 deletion .secrets.baseline
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,16 @@
"is_verified": false,
"line_number": 20
}
],
"src/utils/middleware/csrf.middleware.js": [
{
"type": "Secret Keyword",
"filename": "src/utils/middleware/csrf.middleware.js",
"hashed_secret": "fb23222a82d3fc0120ff287e546fee1be335c81a",
"is_verified": false,
"line_number": 9
}
]
},
"generated_at": "2024-07-22T14:03:26Z"
"generated_at": "2024-12-16T16:47:12Z"
}
1 change: 1 addition & 0 deletions karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ module.exports = karma => karma.set({
exclude: [
'**/axios-base-client.test.js',
'analytics/**/*.js',
'utils/middleware/**/*.js',
'**/errors.test.js'
],
plugins: [
Expand Down
135 changes: 135 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"prepare": "npm run transpile && npm run browserify-analytics",
"test": "npm run jest-tests npm && npm run karma-tests",
"karma-tests": "karma start",
"jest-tests": "jest src/analytics src/utils/axios-base-client/axios-base-client.test.js",
"jest-tests": "jest src/analytics src/utils/axios-base-client/axios-base-client.test.js src/utils/middleware/*.test.js",
"lint": "standard --fix"
},
"repository": {
Expand Down Expand Up @@ -112,6 +112,7 @@
"@babel/cli": "^7.4.4",
"@babel/core": "^7.4.5",
"@babel/preset-env": "^7.4.5",
"@types/express": "^5.0.0",
"babelify": "^10.0.0",
"brfs": "^2.0.2",
"browser-env": "^3.2.6",
Expand Down Expand Up @@ -144,6 +145,7 @@
},
"dependencies": {
"axios": "^1.6.5",
"csrf": "^3.1.0",
"lodash": "4.17.21",
"moment-timezone": "0.5.43",
"rfc822-validate": "1.0.0",
Expand Down
79 changes: 79 additions & 0 deletions src/utils/middleware/csrf.middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
const csrf = require('csrf')

/**
* @param logger {Logger}
* @param sessionKey {string}
* @param secretKey {string}
* @param tokenKey {string}
*/
const configureCsrfMiddleware = (logger, sessionKey, secretKey = 'csrfSecret', tokenKey = 'csrfToken') => {
logger.debug('--- CSRF middleware configuration ---')
logger.debug(`Secret is set at req.[${sessionKey}][${secretKey}]`)
logger.debug(`Token is checked at req.body|query[${tokenKey}]`)
logger.debug('-------------------------------------')
/**
* @param csrfToken {string}
* @param csrfSecret {string}
* @param req {e.Request}
*/
const csrfValid = (csrfToken, csrfSecret, req) => {
if (!csrfSecret) {
logger.debug('CSRF secret not found when validating token')
return false
}
if (!['put', 'post'].includes(req.method.toLowerCase())) {
return true
}
return csrf().verify(csrfSecret, csrfToken)
}

/**
* @param req {e.Request}
* @param res {e.Response}
* @param next {e.NextFunction}
*/
const setSecret = (req, res, next) => {
const csrfSecret = req[sessionKey][secretKey]
if (!csrfSecret) {
logger.debug('Synchronising CSRF secret')
req[sessionKey][secretKey] = csrf().secretSync()
}
next()
}

/**
* @param req {e.Request}
* @param res {e.Response}
* @param next {e.NextFunction}
*/
const checkToken = (req, res, next) => {
const csrfSecret = req[sessionKey][secretKey]
const csrfToken = (req.body && req.body[tokenKey]) || (req.query && req.query[tokenKey])
if (!csrfValid(csrfToken, csrfSecret, req)) {
next(new Error('Invalid CSRF token'))
} else {
next()
}
}

/**
* @param req {e.Request}
* @param res {e.Response}
* @param next {e.NextFunction}
*/
const generateToken = (req, res, next) => {
const csrfSecret = req[sessionKey][secretKey]
res.locals.csrf = csrf().create(csrfSecret)
next()
}

return {
setSecret,
checkToken,
generateToken
}
}

module.exports = {
configureCsrfMiddleware
}
Loading

0 comments on commit 74ecebb

Please sign in to comment.