From 6bfefa02ac1e830aae3c52bf762ef39fec6c29e3 Mon Sep 17 00:00:00 2001 From: Thibaut Patel Date: Fri, 14 May 2021 17:38:28 +0200 Subject: [PATCH] Added a CSRF bypass to enable OAuth issue https://github.com/TryGhost/Team/issues/614 --- ghost/session-service/lib/SessionService.js | 5 +- .../test/SessionService.test.js | 80 ++++++++++++++++++- ghost/session-service/types/index.d.ts | 4 +- 3 files changed, 85 insertions(+), 4 deletions(-) diff --git a/ghost/session-service/lib/SessionService.js b/ghost/session-service/lib/SessionService.js index 306a766055..5786fb5862 100644 --- a/ghost/session-service/lib/SessionService.js +++ b/ghost/session-service/lib/SessionService.js @@ -119,7 +119,10 @@ module.exports = function createSessionService({getSession, findUserById, getOri } const session = await getSession(req, res); - cookieCsrfProtection(req, session); + // Enable CSRF bypass (useful for OAuth for example) + if (!res || !res.locals || !res.locals.bypassCsrfProtection) { + cookieCsrfProtection(req, session); + } if (!session || !session.user_id) { return null; diff --git a/ghost/session-service/test/SessionService.test.js b/ghost/session-service/test/SessionService.test.js index 0e432e046f..d222f1d3c0 100644 --- a/ghost/session-service/test/SessionService.test.js +++ b/ghost/session-service/test/SessionService.test.js @@ -52,5 +52,83 @@ describe('SessionService', function () { await sessionService.destroyCurrentSession(req, res); should.ok(req.session.destroy.calledOnce); }); -}); + it('Throws an error when the csrf verification fails', async function () { + const getSession = async (req) => { + if (req.session) { + return req.session; + } + req.session = { + origin: 'origin' + }; + return req.session; + }; + const findUserById = sinon.spy(async ({id}) => ({id})); + const getOriginOfRequest = sinon.stub().returns('other-origin'); + + const sessionService = SessionService({ + getSession, + findUserById, + getOriginOfRequest + }); + + const req = Object.create(express.request, { + ip: { + value: '0.0.0.0' + }, + headers: { + value: { + cookie: 'thing' + } + }, + get: { + value: () => 'Fake' + } + }); + const res = Object.create(express.response); + + const error = `Request made from incorrect origin. Expected 'origin' received 'other-origin'.`; + + await sessionService.getUserForSession(req, res).should.be.rejectedWith(error); + }); + + it('Doesn\'t throw an error when the csrf verification fails when bypassed', async function () { + const getSession = async (req) => { + if (req.session) { + return req.session; + } + req.session = { + origin: 'origin' + }; + return req.session; + }; + const findUserById = sinon.spy(async ({id}) => ({id})); + const getOriginOfRequest = sinon.stub().returns('other-origin'); + + const sessionService = SessionService({ + getSession, + findUserById, + getOriginOfRequest + }); + + const req = Object.create(express.request, { + ip: { + value: '0.0.0.0' + }, + headers: { + value: { + cookie: 'thing' + } + }, + get: { + value: () => 'Fake' + } + }); + const res = Object.create(express.response); + res.locals = { + bypassCsrfProtection: true + }; + + await sessionService.getUserForSession(req, res).should.be.fulfilled(); + }); +}); diff --git a/ghost/session-service/types/index.d.ts b/ghost/session-service/types/index.d.ts index c0bf43bc18..7bda0dbcab 100644 --- a/ghost/session-service/types/index.d.ts +++ b/ghost/session-service/types/index.d.ts @@ -1,8 +1,8 @@ declare function _exports({ getSession, findUserById, getOriginOfRequest }: { - getSession: (req: import("express").Request, res: import("express").Response) => Promise; + getSession: (req: import("express").Request>, res: import("express").Response>) => Promise; findUserById: (data: { id: string; }) => Promise; - getOriginOfRequest: (req: import("express").Request) => string; + getOriginOfRequest: (req: import("express").Request>) => string; }): import("./lib/SessionService").SessionService; export = _exports;