SessionStoreManager.js 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. const Metrics = require('@overleaf/metrics')
  2. const logger = require('@overleaf/logger')
  3. function computeValidationToken(req) {
  4. // this should be a deterministic function of the client-side sessionID,
  5. // prepended with a version number in case we want to change it later
  6. return 'v1:' + req.sessionID.slice(-4)
  7. }
  8. function checkValidationToken(req) {
  9. if (req.session) {
  10. const sessionToken = req.session.validationToken
  11. if (sessionToken) {
  12. const clientToken = computeValidationToken(req)
  13. // Reject invalid sessions. If you change the method for computing the
  14. // token (above) then you need to either check or ignore previous
  15. // versions of the token.
  16. if (sessionToken === clientToken) {
  17. Metrics.inc('security.session', 1, { status: 'ok' })
  18. return true
  19. } else {
  20. logger.error(
  21. {
  22. sessionToken: sessionToken,
  23. clientToken: clientToken,
  24. },
  25. 'session token validation failed'
  26. )
  27. Metrics.inc('security.session', 1, { status: 'error' })
  28. return false
  29. }
  30. } else {
  31. Metrics.inc('security.session', 1, { status: 'missing' })
  32. }
  33. }
  34. return true // fallback to allowing session
  35. }
  36. module.exports = {
  37. enableValidationToken(sessionStore) {
  38. // generate an identifier from the sessionID for every new session
  39. const originalGenerate = sessionStore.generate
  40. sessionStore.generate = function (req) {
  41. originalGenerate(req)
  42. // add the validation token as a property that cannot be overwritten
  43. Object.defineProperty(req.session, 'validationToken', {
  44. value: computeValidationToken(req),
  45. enumerable: true,
  46. writable: false,
  47. })
  48. Metrics.inc('security.session', 1, { status: 'new' })
  49. }
  50. },
  51. validationMiddleware(req, res, next) {
  52. if (!req.session.noSessionCallback) {
  53. if (!checkValidationToken(req)) {
  54. // the session must exist for it to fail validation
  55. return req.session.destroy(() => {
  56. return next(new Error('invalid session'))
  57. })
  58. }
  59. }
  60. next()
  61. },
  62. hasValidationToken(req) {
  63. if (req && req.session && req.session.validationToken) {
  64. return true
  65. } else {
  66. return false
  67. }
  68. },
  69. }