From 7ae6635d1686afa193bd1417abd9c9891aa0b71a Mon Sep 17 00:00:00 2001 From: "Kolega.dev" Date: Thu, 12 Feb 2026 02:27:36 +0000 Subject: [PATCH] fix: validate loginRedirect cookie to prevent open redirect (#7923) The loginRedirect cookie value was used directly in res.redirect() and window.location.replace() without validation, allowing redirection to arbitrary external URLs. Added validation to ensure the redirect target is a relative path before use. Co-authored-by: kolega.dev --- client/components/login.vue | 14 ++++++++++---- server/controllers/auth.js | 14 ++++++++++---- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/client/components/login.vue b/client/components/login.vue index bf9b26c22..0bbaa2a5e 100644 --- a/client/components/login.vue +++ b/client/components/login.vue @@ -644,16 +644,22 @@ export default { Cookies.set('jwt', respObj.jwt, { expires: 365, secure: window.location.protocol === 'https:' }) _.delay(() => { const loginRedirect = Cookies.get('loginRedirect') + const isValidRedirect = loginRedirect && loginRedirect.startsWith('/') && !loginRedirect.startsWith('//') && !loginRedirect.includes('://') if (loginRedirect === '/' && respObj.redirect) { Cookies.remove('loginRedirect') window.location.replace(respObj.redirect) - } else if (loginRedirect) { + } else if (isValidRedirect) { Cookies.remove('loginRedirect') window.location.replace(loginRedirect) - } else if (respObj.redirect) { - window.location.replace(respObj.redirect) } else { - window.location.replace('/') + if (loginRedirect) { + Cookies.remove('loginRedirect') + } + if (respObj.redirect) { + window.location.replace(respObj.redirect) + } else { + window.location.replace('/') + } } }, 1000) } diff --git a/server/controllers/auth.js b/server/controllers/auth.js index 7a947338d..733486be0 100644 --- a/server/controllers/auth.js +++ b/server/controllers/auth.js @@ -73,16 +73,22 @@ router.all('/login/:strategy/callback', async (req, res, next) => { res.cookie('jwt', authResult.jwt, commonHelper.getCookieOpts()) const loginRedirect = req.cookies['loginRedirect'] + const isValidRedirect = loginRedirect && loginRedirect.startsWith('/') && !loginRedirect.startsWith('//') && !loginRedirect.includes('://') if (loginRedirect === '/' && authResult.redirect) { res.clearCookie('loginRedirect') res.redirect(authResult.redirect) - } else if (loginRedirect) { + } else if (isValidRedirect) { res.clearCookie('loginRedirect') res.redirect(loginRedirect) - } else if (authResult.redirect) { - res.redirect(authResult.redirect) } else { - res.redirect('/') + if (loginRedirect) { + res.clearCookie('loginRedirect') + } + if (authResult.redirect) { + res.redirect(authResult.redirect) + } else { + res.redirect('/') + } } } catch (err) { next(err)