From 62e162cf1e735e42462be1db9b4954b5a69accdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 15 May 2023 11:55:18 -0400 Subject: [PATCH] fix: backport ws token fix --- public/src/sockets.js | 3 +++ src/middleware/csrf.js | 15 +++++++++++++-- src/middleware/index.js | 2 +- src/socket.io/index.js | 38 +++++++++++++++++++++++++------------- test/helpers/index.js | 5 ++++- test/socket.io.js | 2 +- 6 files changed, 47 insertions(+), 18 deletions(-) diff --git a/public/src/sockets.js b/public/src/sockets.js index 2927c21619..d12f32c332 100644 --- a/public/src/sockets.js +++ b/public/src/sockets.js @@ -15,6 +15,9 @@ app = window.app || {}; reconnectionDelay: config.reconnectionDelay, transports: config.socketioTransports, path: config.relative_path + '/socket.io', + query: { + _csrf: config.csrf_token, + }, }; window.socket = io(config.websocketAddress, ioParams); diff --git a/src/middleware/csrf.js b/src/middleware/csrf.js index 4ad824bca1..1cfbb8a3e1 100644 --- a/src/middleware/csrf.js +++ b/src/middleware/csrf.js @@ -5,11 +5,22 @@ const { csrfSync } = require('csrf-sync'); const { generateToken, csrfSynchronisedProtection, + isRequestValid, } = csrfSync({ - size: 64 + getTokenFromRequest: (req) => { + if (req.headers['x-csrf-token']) { + return req.headers['x-csrf-token']; + } else if (req.body && req.body.csrf_token) { + return req.body.csrf_token; + } else if (req.query) { + return req.query._csrf; + } + }, + size: 64, }); module.exports = { generateToken, csrfSynchronisedProtection, -}; + isRequestValid, +}; \ No newline at end of file diff --git a/src/middleware/index.js b/src/middleware/index.js index f81e96983e..d98064a1d5 100644 --- a/src/middleware/index.js +++ b/src/middleware/index.js @@ -2,11 +2,11 @@ const async = require('async'); const path = require('path'); -const { csrfSynchronisedProtection } = require('./csrf'); const validator = require('validator'); const nconf = require('nconf'); const toobusy = require('toobusy-js'); const util = require('util'); +const { csrfSynchronisedProtection } = require('./csrf'); const plugins = require('../plugins'); const meta = require('../meta'); diff --git a/src/socket.io/index.js b/src/socket.io/index.js index 963267ed9a..d457afefbc 100644 --- a/src/socket.io/index.js +++ b/src/socket.io/index.js @@ -34,13 +34,25 @@ Sockets.init = async function (server) { } } - io.use(authorize); - io.on('connection', onConnection); const opts = { transports: nconf.get('socket.io:transports') || ['polling', 'websocket'], cookie: false, + allowRequest: (req, callback) => { + authorize(req, (err) => { + if (err) { + return callback(err); + } + const csrf = require('../middleware/csrf'); + const isValid = csrf.isRequestValid({ + session: req.session || {}, + query: req._query, + headers: req.headers, + }); + callback(null, isValid); + }); + }, }; /* * Restrict socket.io listener to cookie domain. If none is set, infer based on url. @@ -62,7 +74,11 @@ Sockets.init = async function (server) { }; function onConnection(socket) { - socket.ip = (socket.request.headers['x-forwarded-for'] || socket.request.connection.remoteAddress || '').split(',')[0]; + socket.uid = socket.request.uid; + socket.ip = ( + socket.request.headers['x-forwarded-for'] || + socket.request.connection.remoteAddress || '' + ).split(',')[0]; socket.request.ip = socket.ip; logger.io_one(socket, socket.uid); @@ -231,9 +247,7 @@ async function validateSession(socket, errorMsg) { const cookieParserAsync = util.promisify((req, callback) => cookieParser(req, {}, err => callback(err))); -async function authorize(socket, callback) { - const { request } = socket; - +async function authorize(request, callback) { if (!request) { return callback(new Error('[[error:not-authorized]]')); } @@ -246,15 +260,13 @@ async function authorize(socket, callback) { }); const sessionData = await getSessionAsync(sessionId); - + request.session = sessionData; + let uid = 0; if (sessionData && sessionData.passport && sessionData.passport.user) { - request.session = sessionData; - socket.uid = parseInt(sessionData.passport.user, 10); - } else { - socket.uid = 0; + uid = parseInt(sessionData.passport.user, 10); } - request.uid = socket.uid; - callback(); + request.uid = uid; + callback(null, uid); } Sockets.in = function (room) { diff --git a/test/helpers/index.js b/test/helpers/index.js index dd52e35f80..4b536db004 100644 --- a/test/helpers/index.js +++ b/test/helpers/index.js @@ -95,7 +95,7 @@ helpers.logoutUser = function (jar, callback) { }); }; -helpers.connectSocketIO = function (res, callback) { +helpers.connectSocketIO = function (res, csrf_token, callback) { const io = require('socket.io-client'); let cookies = res.headers['set-cookie']; cookies = cookies.filter(c => /express.sid=[^;]+;/.test(c)); @@ -106,6 +106,9 @@ helpers.connectSocketIO = function (res, callback) { Origin: nconf.get('url'), Cookie: cookie, }, + query: { + _csrf: csrf_token, + }, }); socket.on('connect', () => { diff --git a/test/socket.io.js b/test/socket.io.js index 3628e3b0d0..33377e8bb3 100644 --- a/test/socket.io.js +++ b/test/socket.io.js @@ -73,7 +73,7 @@ describe('socket.io', () => { }, (err, res) => { assert.ifError(err); - helpers.connectSocketIO(res, (err, _io) => { + helpers.connectSocketIO(res, body.csrf_token, (err, _io) => { io = _io; assert.ifError(err);