Moderate severityNVD Advisory· Published Dec 16, 2021· Updated Aug 4, 2024
Open redirect in nextjs-auth0
CVE-2021-43812
Description
The Auth0 Next.js SDK is a library for implementing user authentication in Next.js applications. Versions before 1.6.2 do not filter out certain returnTo parameter values from the login url, which expose the application to an open redirect vulnerability. Users are advised to upgrade as soon as possible. There are no known workarounds for this issue.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
@auth0/nextjs-auth0npm | < 1.6.2 | 1.6.2 |
Affected products
1- Range: < 1.6.2
Patches
10bbd9f8a0c93Enforce configured host on user supplied returnTo (#557)
6 files changed · +571 −33
src/handlers/login.ts+10 −7 modified@@ -1,8 +1,8 @@ import { NextApiResponse, NextApiRequest } from 'next'; import { AuthorizationParameters, HandleLogin as BaseHandleLogin } from '../auth0-session'; -import isSafeRedirect from '../utils/url-helpers'; +import toSafeRedirect from '../utils/url-helpers'; import { assertReqRes } from '../utils/assert'; -import { NextConfig } from '../config'; +import { BaseConfig, NextConfig } from '../config'; import { HandlerError } from '../utils/errors'; /** @@ -117,16 +117,19 @@ export type HandleLogin = (req: NextApiRequest, res: NextApiResponse, options?: /** * @ignore */ -export default function handleLoginFactory(handler: BaseHandleLogin, nextConfig: NextConfig): HandleLogin { +export default function handleLoginFactory( + handler: BaseHandleLogin, + nextConfig: NextConfig, + baseConfig: BaseConfig +): HandleLogin { return async (req, res, options = {}): Promise<void> => { try { assertReqRes(req, res); if (req.query.returnTo) { - const returnTo = Array.isArray(req.query.returnTo) ? req.query.returnTo[0] : req.query.returnTo; + const dangerousReturnTo = Array.isArray(req.query.returnTo) ? req.query.returnTo[0] : req.query.returnTo; + const safeBaseUrl = new URL(options.authorizationParams?.redirect_uri || baseConfig.baseURL); - if (!isSafeRedirect(returnTo)) { - throw new Error('Invalid value provided for returnTo, must be a relative url'); - } + const returnTo = toSafeRedirect(dangerousReturnTo, safeBaseUrl); options = { ...options, returnTo }; }
src/index.ts+1 −1 modified@@ -77,7 +77,7 @@ export const initAuth0: InitAuth0 = (params) => { const getAccessToken = accessTokenFactory(nextConfig, getClient, sessionCache); const withApiAuthRequired = withApiAuthRequiredFactory(sessionCache); const withPageAuthRequired = withPageAuthRequiredFactory(nextConfig.routes.login, getSession); - const handleLogin = loginHandler(baseHandleLogin, nextConfig); + const handleLogin = loginHandler(baseHandleLogin, nextConfig, baseConfig); const handleLogout = logoutHandler(baseHandleLogout); const handleCallback = callbackHandler(baseHandleCallback, nextConfig); const handleProfile = profileHandler(getClient, getAccessToken, sessionCache);
src/utils/url-helpers.ts+11 −10 modified@@ -1,16 +1,17 @@ /** * Helper which tests if a URL can safely be redirected to. Requires the URL to be relative. - * @param url + * @param dangerousRedirect + * @param safeBaseUrl */ -export default function isSafeRedirect(url: string): boolean { - if (typeof url !== 'string') { - throw new TypeError(`Invalid url: ${url}`); +export default function toSafeRedirect(dangerousRedirect: string, safeBaseUrl: URL): string | undefined { + let url: URL; + try { + url = new URL(dangerousRedirect, safeBaseUrl); + } catch (e) { + return undefined; } - - // Prevent open redirects using the //foo.com format (double forward slash). - if (/\/\//.test(url)) { - return false; + if (url.origin === safeBaseUrl.origin) { + return url.toString(); } - - return !/^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(url); + return undefined; }
tests/fixtures/open-redirect-payloads.json+487 −0 added@@ -0,0 +1,487 @@ +[ + "javascript:alert(1)", + "java%0d%0ascript%0d%0a:alert(0)", + "javascript://%250Aalert(1)", + "javascript://%250Aalert(1)//?1", + "javascript://%250A1?alert(1):0", + "%09Jav%09ascript:alert(document.domain)", + "javascript://%250Alert(document.location=document.cookie)", + "/%09/javascript:alert(1);", + "/%09/javascript:alert(1)", + "//%5cjavascript:alert(1);", + "//%5cjavascript:alert(1)", + "/%5cjavascript:alert(1);", + "/%5cjavascript:alert(1)", + "javascript://%0aalert(1)", + "<>javascript:alert(1);", + "//javascript:alert(1);", + "//javascript:alert(1)", + "/javascript:alert(1);", + "/javascript:alert(1)", + "javascript:alert(1)", + "javascript:alert(1);", + "javascript:alert(1)", + "javascripT://anything%0D%0A%0D%0Awindow.alert(document.cookie)", + "javascript:confirm(1)", + "javascript://https://whitelisted.com/?z=%0Aalert(1)", + "javascript:prompt(1)", + "jaVAscript://whitelisted.com//%0d%0aalert(1);//", + "javascript://whitelisted.com?%a0alert%281%29", + "/x:1/:///%01javascript:alert(document.cookie)/", + "<>//Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "//;@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "/////Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/", + "/////Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "////Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ//", + "////Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/", + "///;@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "///Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ//", + "///Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/", + "///Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "///Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/", + "//Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ//", + "//Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/", + "//Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "/.Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "//Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/", + "/〱Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + ".Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "//Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/", + "〱Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "//Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ%00。Pⓦ", + "%01https://Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "%01https://google.com", + "////%09/Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "///%09/Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "//%09/Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "/%09/Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "////%09/google.com", + "///%09/google.com", + "//%09/google.com", + "/%09/google.com", + "////%09/whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "///%09/whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "//%09/whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "/%09/whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "////%09/whitelisted.com@google.com", + "///%09/whitelisted.com@google.com", + "//%09/whitelisted.com@google.com", + "/%09/whitelisted.com@google.com", + "&%0d%0a1Location:https://google.com", + "%19Jav%09asc%09ript:https%20://whitelisted.com/%250Aconfirm%25281%2529", + "////216.58.214.206", + "///216.58.214.206", + "//216.58.214.206", + "/\\216.58.214.206", + "/216.58.214.206", + "216.58.214.206", + "////Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2e%2e", + "///Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2e%2e", + "////Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2e%2e%2f", + "///Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2e%2e%2f", + "//Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2e%2e%2f", + "////Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2f..", + "///Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2f..", + "//Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2f..", + "%2f216.58.214.206//", + "%2f216.58.214.206", + "%2f216.58.214.206%2f%2f", + "////Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2f%2e%2e", + "///Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2f%2e%2e", + "//Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2f%2e%2e", + "/Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2f%2e%2e", + "//%2f%2fⓁ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "/%2f%2fⓁ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "%2f$2f216.58.214.206", + "$2f%2f216.58.214.206%2f%2f", + "%2f$2f3627734734", + "$2f%2f3627734734%2f%2f", + "//%2f%2fgoogle.com", + "/%2f%2fgoogle.com", + "$2f%2fgoogle.com", + "%2f$2fgoogle.com", + "$2f%2fgoogle.com%2f%2f", + "%2f3627734734//", + "%2f3627734734", + "%2f3627734734%2f%2f", + "/%2f%5c%2f%67%6f%6f%67%6c%65%2e%63%6f%6d/", + "/%2f%5c%2f%6c%6f%63%61%6c%64%6f%6d%61%69%6e%2e%70%77/", + "%2fgoogle.com//", + "%2fgoogle.com", + "%2fgoogle.com%2f%2f", + "////3627734734", + "///3627734734", + "//3627734734", + "/3627734734", + "3627734734", + "//3H6k7lIAiqjfNeN@whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/", + "//3H6k7lIAiqjfNeN@whitelisted.com+@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/", + "//3H6k7lIAiqjfNeN@whitelisted.com@google.com/", + "//3H6k7lIAiqjfNeN@whitelisted.com+@google.com/", + "////%5cⓁ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "///%5cⓁ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "//%5cⓁ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "/%5cⓁ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "////%5cgoogle.com", + "///%5cgoogle.com", + "//%5cgoogle.com", + "/%5cgoogle.com", + "////%5cwhitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "///%5cwhitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "//%5cwhitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "/%5cwhitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "////%5cwhitelisted.com@google.com", + "///%5cwhitelisted.com@google.com", + "//%5cwhitelisted.com@google.com", + "/%5cwhitelisted.com@google.com", + "/%68%74%74%70%3a%2f%2f%67%6f%6f%67%6c%65%2e%63%6f%6d", + "%68%74%74%70%3a%2f%2f%67%6f%6f%67%6c%65%2e%63%6f%6d", + "%68%74%74%70%73%3a%2f%2f%6c%6f%63%61%6c%64%6f%6d%61%69%6e%2e%70%77", + "//Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ:80?@whitelisted.com/", + "//Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ:80#@whitelisted.com/", + "\";alert(0);//", + "data:text/html;base64,PHNjcmlwdD5hbGVydCgiSGVsbG8iKTs8L3NjcmlwdD4=", + "data:text/html;base64,PHNjcmlwdD5hbGVydCgiWFNTIik7PC9zY3JpcHQ+Cg==", + "data:text/html;base64,PHNjcmlwdD5hbGVydCgiWFNTIik8L3NjcmlwdD4=", + "data:whitelisted.com;text/html;charset=UTF-8,<html><script>document.write(document.domain);</script><iframe/src=xxxxx>aaaa</iframe></html>", + "//Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ%E3%80%82pw", + "//google%00.com", + "/google%252ecom", + "google%252ecom", + "<>//google.com", + "/<>//google.com", + "//;@google.com", + "///;@google.com", + "/////google.com/", + "/////google.com", + "////;@google.com", + "////google.com//", + "////google.com/", + "////google.com", + "///;@google.com", + "///google.com//", + "///google.com/", + "///google.com", + "///google.com/", + "//google.com", + "//google.com//", + "//google.com/", + "//google.com", + "/.google.com", + "///google.com/", + "//google.com/", + "//google.com", + "/google.com", + "/〱google.com", + "/google.com", + "../google.com", + ".google.com", + "@google.com", + "//google.com/", + "〱google.com", + "google.com", + "google.com%23@whitelisted.com", + "////google.com/%2e%2e", + "///google.com/%2e%2e", + "//google.com/%2e%2e", + "/google.com/%2e%2e", + "//google.com/%2E%2E", + "////google.com/%2e%2e%2f", + "///google.com/%2e%2e%2f", + "//google.com/%2e%2e%2f", + "////google.com/%2f..", + "///google.com/%2f..", + "//google.com/%2f..", + "//google.com/%2F..", + "/google.com/%2F..", + "////google.com/%2f%2e%2e", + "///google.com/%2f%2e%2e", + "//google.com/%2f%2e%2e", + "/google.com/%2f%2e%2e", + "//google.com//%2F%2E%2E", + "//google.com:80?@whitelisted.com/", + "//google.com:80#@whitelisted.com/", + "google.com/.jpg", + "//google.com\twhitelisted.com/", + "//google.com/whitelisted.com", + "//google.com@whitelisted.com", + "google.com/whitelisted.com", + "//google%E3%80%82com", + "/http://Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "/http:/Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "http://;@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "http://.Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "http:/Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "http:Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "http://00330.00072.0000326.00000316", + "http:00330.00072.0000326.00000316", + "http://00330.0x3a.54990", + "http:00330.0x3a.54990", + "http://00330.3856078", + "http:00330.3856078", + "http://0330.072.0326.0316", + "http:0330.072.0326.0316", + "http:%0a%0dⓁ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "http:%0a%0dgoogle.com", + "http://0xd8.072.54990", + "http:0xd8.072.54990", + "http://0xd8.0x3a.0xd6.0xce", + "http:0xd8.0x3a.0xd6.0xce", + "http://0xd8.3856078", + "http:0xd8.3856078", + "http://0xd83ad6ce", + "http:0xd83ad6ce", + "http://[::216.58.214.206]", + "http:[::216.58.214.206]", + "http://Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ%23.whitelisted.com/", + "http://Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ%2f%2f.whitelisted.com/", + "http://3627734734", + "http:3627734734", + "http://Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ%3F.whitelisted.com/", + "http://3H6k7lIAiqjfNeN@00330.00072.0000326.00000316", + "http:3H6k7lIAiqjfNeN@00330.00072.0000326.00000316", + "http://3H6k7lIAiqjfNeN@00330.0x3a.54990", + "http:3H6k7lIAiqjfNeN@00330.0x3a.54990", + "http://3H6k7lIAiqjfNeN@00330.3856078", + "http:3H6k7lIAiqjfNeN@00330.3856078", + "http://3H6k7lIAiqjfNeN@0330.072.0326.0316", + "http:3H6k7lIAiqjfNeN@0330.072.0326.0316", + "http://3H6k7lIAiqjfNeN@0xd8.072.54990", + "http:3H6k7lIAiqjfNeN@0xd8.072.54990", + "http://3H6k7lIAiqjfNeN@0xd8.0x3a.0xd6.0xce", + "http:3H6k7lIAiqjfNeN@0xd8.0x3a.0xd6.0xce", + "http://3H6k7lIAiqjfNeN@0xd8.3856078", + "http:3H6k7lIAiqjfNeN@0xd8.3856078", + "http://3H6k7lIAiqjfNeN@0xd83ad6ce", + "http:3H6k7lIAiqjfNeN@0xd83ad6ce", + "http://3H6k7lIAiqjfNeN@[::216.58.214.206]", + "http:3H6k7lIAiqjfNeN@[::216.58.214.206]", + "http://3H6k7lIAiqjfNeN@3627734734", + "http:3H6k7lIAiqjfNeN@3627734734", + "http://3H6k7lIAiqjfNeN@472.314.470.462", + "http:3H6k7lIAiqjfNeN@472.314.470.462", + "http://3H6k7lIAiqjfNeN@[::ffff:216.58.214.206]", + "http:3H6k7lIAiqjfNeN@[::ffff:216.58.214.206]", + "http://3H6k7lIAiqjfNeN@whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/", + "http://3H6k7lIAiqjfNeN@whitelisted.com+@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/", + "http://3H6k7lIAiqjfNeN@whitelisted.com@google.com/", + "http://3H6k7lIAiqjfNeN@whitelisted.com+@google.com/", + "http://472.314.470.462", + "http:472.314.470.462", + "http://Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ%5c%5c.whitelisted.com/", + "/http://%67%6f%6f%67%6c%65%2e%63%6f%6d", + "http://%67%6f%6f%67%6c%65%2e%63%6f%6d", + "http://Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ:80?@whitelisted.com/", + "http://Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ:80#@whitelisted.com/", + "http://[::ffff:216.58.214.206]", + "http:[::ffff:216.58.214.206]", + "/http://google.com", + "/http:/google.com", + "http://;@google.com", + "http://.google.com", + "http://google.com", + "http://google.com", + "http:/google.com", + "http:google.com", + "http://google.com%23.whitelisted.com/", + "http://google.com%2f%2f.whitelisted.com/", + "http://google.com%3F.whitelisted.com/", + "http://google.com%5c%5c.whitelisted.com/", + "http://google.com:80?@whitelisted.com/", + "http://google.com:80#@whitelisted.com/", + "http://google.com\twhitelisted.com/", + "//https://Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ//", + "/https://Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/", + "https://Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ//", + "https://Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/", + "https://Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "https:Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "https://%09/Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "/https://%09/google.com", + "https://%09/google.com", + "https://%09/whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "https://%09/whitelisted.com@google.com", + "https://%0a%0dⓁ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "https://%0a%0dgoogle.com", + "//https:///Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2e%2e", + "/https://Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2e%2e", + "https:///Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2e%2e", + "//https://Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2e%2e%2f", + "https://Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2e%2e%2f", + "/https://Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2f..", + "https://Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2f..", + "/https:///Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2f%2e%2e", + "/https://Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2f%2e%2e", + "https:///Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2f%2e%2e", + "https://Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2f%2e%2e", + "https%3a%2f%2fgoogle.com%2f", + "/https://%5cⓁ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "/https:/%5cⓁ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/", + "https://%5cⓁ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "https:/%5cⓁ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/", + "/https://%5cgoogle.com", + "/https:/%5cgoogle.com/", + "https://%5cgoogle.com", + "https:/%5cgoogle.com/", + "/https://%5cwhitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "https://%5cwhitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "/https://%5cwhitelisted.com@google.com", + "https://%5cwhitelisted.com@google.com", + "https://%6c%6f%63%61%6c%64%6f%6d%61%69%6e%2e%70%77", + "//https://google.com//", + "/https://google.com//", + "/https://google.com/", + "/https://google.com", + "/https:google.com", + "https://////google.com", + "https://google.com//", + "https://google.com/", + "https://google.com", + "https:/google.com", + "https:google.com", + "//https:///google.com/%2e%2e", + "/https://google.com/%2e%2e", + "https:///google.com/%2e%2e", + "//https://google.com/%2e%2e%2f", + "https://google.com/%2e%2e%2f", + "/https://google.com/%2f..", + "https://google.com/%2f..", + "/https:///google.com/%2f%2e%2e", + "/https://google.com/%2f%2e%2e", + "https:///google.com/%2f%2e%2e", + "https://google.com/%2f%2e%2e", + "https://:@google.com@whitelisted.com", + "https://google.com?whitelisted.com", + "https://google.com/whitelisted.com", + "https://google.comwhitelisted.com", + "https://google.com#whitelisted.com", + "https://google%E3%80%82com", + "//https://whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ//", + "/https://whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/", + "https://:@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ@whitelisted.com", + "https://Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/whitelisted.com", + "https://whitelisted.com;@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "https://whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ//", + "https://whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/", + "https://whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "/https://whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2e%2e", + "https:///whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2e%2e", + "//https://whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2e%2e%2f", + "https://whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2e%2e%2f", + "/https://whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2f..", + "https://whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2f..", + "/https:///whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2f%2e%2e", + "/https://whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2f%2e%2e", + "https:///whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2f%2e%2e", + "https://whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2f%2e%2e", + "//https://whitelisted.com@google.com//", + "/https://whitelisted.com@google.com/", + "https://whitelisted.com;@google.com", + "https://whitelisted.com.google.com", + "https://whitelisted.com@google.com//", + "https://whitelisted.com@google.com/", + "https://whitelisted.com@google.com", + "/https://whitelisted.com@google.com/%2e%2e", + "https:///whitelisted.com@google.com/%2e%2e", + "//https://whitelisted.com@google.com/%2e%2e%2f", + "https://whitelisted.com@google.com/%2e%2e%2f", + "/https://whitelisted.com@google.com/%2f..", + "https://whitelisted.com@google.com/%2f..", + "/https:///whitelisted.com@google.com/%2f%2e%2e", + "/https://whitelisted.com@google.com/%2f%2e%2e", + "https:///whitelisted.com@google.com/%2f%2e%2e", + "https://whitelisted.com@google.com/%2f%2e%2e", + "/https://whitelisted.com@google.com/%2f.//whitelisted.com@google.com/%2f..", + "https://whitelisted.com/https://Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/", + "https://whitelisted.com/https://google.com/", + "@https://www.google.com", + "http://Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ\twhitelisted.com/", + "http://whitelisted.com@00330.00072.0000326.00000316", + "http:whitelisted.com@00330.00072.0000326.00000316", + "http://whitelisted.com@00330.0x3a.54990", + "http:whitelisted.com@00330.0x3a.54990", + "http://whitelisted.com@00330.3856078", + "http:whitelisted.com@00330.3856078", + "http://whitelisted.com@0330.072.0326.0316", + "http:whitelisted.com@0330.072.0326.0316", + "http://whitelisted.com@0xd8.072.54990", + "http:whitelisted.com@0xd8.072.54990", + "http://whitelisted.com@0xd8.0x3a.0xd6.0xce", + "http:whitelisted.com@0xd8.0x3a.0xd6.0xce", + "http://whitelisted.com@0xd8.3856078", + "http:whitelisted.com@0xd8.3856078", + "http://whitelisted.com@0xd83ad6ce", + "http:whitelisted.com@0xd83ad6ce", + "http://whitelisted.com@[::216.58.214.206]", + "http:whitelisted.com@[::216.58.214.206]", + "http://whitelisted.com%2eⓁ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/", + "http://whitelisted.com%2egoogle.com/", + "http://whitelisted.com@3627734734", + "http:whitelisted.com@3627734734", + "http://whitelisted.com@472.314.470.462", + "http:whitelisted.com@472.314.470.462", + "http://whitelisted.com:80%40Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/", + "http://whitelisted.com:80%40google.com/", + "http://whitelisted.com@[::ffff:216.58.214.206]", + "http:whitelisted.com@[::ffff:216.58.214.206]", + "http://whitelisted.com@google.com/", + "http://whitelisted.com+&@google.com#+@whitelisted.com/", + "http://whitelisted.com+&@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ#+@whitelisted.com/", + "http://www.google.com.whitelisted.com", + "http://www.Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ.whitelisted.com", + "ja\nva\tscript\r:alert(1)", + "java%09script:alert(1)", + "java%0ascript:alert(1)", + "java%0d%0ascript%0d%0a:alert(0)", + "java%0dscript:alert(1)", + "Javas%26%2399;ript:alert(1)", + "//Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ\twhitelisted.com/", + "\u006A\u0061\u0076\u0061\u0073\u0063\u0072\u0069\u0070\u0074\u003aalert(1)", + "////whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ//", + "////whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/", + "///whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ//", + "///whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/", + "//Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/whitelisted.com", + "//Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ@whitelisted.com", + "//whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ//", + "//whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/", + "Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/whitelisted.com", + "whitelisted.com;@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ", + "////whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2e%2e", + "///whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2e%2e", + "////whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2e%2e%2f", + "///whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2e%2e%2f", + "//whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2e%2e%2f", + "////whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2f..", + "///whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2f..", + "//whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2f..", + "////whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2f%2e%2e", + "///whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2f%2e%2e", + "//whitelisted.com@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2f%2e%2e", + "/whitelisted.com:80%40google.com", + "whitelisted.com@%E2%80%AE@google.com", + "////whitelisted.com@google.com//", + "////whitelisted.com@google.com/", + "///whitelisted.com@google.com//", + "///whitelisted.com@google.com/", + "//whitelisted.com@google.com//", + "//whitelisted.com@google.com/", + "whitelisted.com;@google.com", + "whitelisted.com.google.com", + "////whitelisted.com@google.com/%2e%2e", + "///whitelisted.com@google.com/%2e%2e", + "////whitelisted.com@google.com/%2e%2e%2f", + "///whitelisted.com@google.com/%2e%2e%2f", + "//whitelisted.com@google.com/%2e%2e%2f", + "////whitelisted.com@google.com/%2f..", + "///whitelisted.com@google.com/%2f..", + "//whitelisted.com@google.com/%2f..", + "////whitelisted.com@google.com/%2f%2e%2e", + "///whitelisted.com@google.com/%2f%2e%2e", + "//whitelisted.com@google.com/%2f%2e%2e", + "//whitelisted.com+&@google.com#+@whitelisted.com/", + "//whitelisted.com@https:///Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ/%2e%2e", + "//whitelisted.com@https:///google.com/%2e%2e", + "//whitelisted.com+&@Ⓛ𝐨𝗰�𝕝ⅆ𝓸ⓜₐℹⓃ。Pⓦ#+@whitelisted.com/" +]
tests/handlers/login.test.ts+43 −5 modified@@ -214,7 +214,7 @@ describe('login handler', () => { const decodedState = decodeState(state.split('.')[0]); expect(decodedState).toEqual({ - returnTo: '/foo' + returnTo: new URL('/foo', withoutApi.baseURL).toString() }); }); @@ -229,7 +229,7 @@ describe('login handler', () => { const decodedState = decodeState(state.split('.')[0]); expect(decodedState).toEqual({ - returnTo: '/foo' + returnTo: new URL('/foo', withoutApi.baseURL).toString() }); }); @@ -239,9 +239,47 @@ describe('login handler', () => { }; const baseUrl = await setup(withoutApi, { loginOptions }); - await expect( - get(baseUrl, '/api/auth/login?returnTo=https://www.google.com', { fullResponse: true }) - ).rejects.toThrow('Invalid value provided for returnTo, must be a relative url'); + const cookieJar = new CookieJar(); + await get(baseUrl, '/api/auth/login?returnTo=https://www.google.com', { cookieJar }); + const { value: state } = getCookie('state', cookieJar, baseUrl) as Cookie; + + const decodedState = decodeState(state.split('.')[0]); + expect(decodedState).toEqual({}); + }); + + test('should allow absolute urls in params of returnTo urls', async () => { + const loginOptions = { + returnTo: '/default-redirect' + }; + const baseUrl = await setup(withoutApi, { loginOptions }); + + const cookieJar = new CookieJar(); + await get(baseUrl, '/api/auth/login?returnTo=/foo?url=https://www.google.com', { cookieJar }); + const { value: state } = getCookie('state', cookieJar, baseUrl) as Cookie; + + const decodedState = decodeState(state.split('.')[0]); + expect(decodedState).toEqual({ + returnTo: new URL('/foo?url=https://www.google.com', withoutApi.baseURL).toString() + }); + }); + + test('should redirect relative to the redirect_uri over the base url', async () => { + const loginOptions = { + returnTo: '/default-redirect', + authorizationParams: { + redirect_uri: 'https://other-org.acme.com/api/auth/callback' + } + }; + const baseUrl = await setup(withoutApi, { loginOptions }); + + const cookieJar = new CookieJar(); + await get(baseUrl, '/api/auth/login?returnTo=/foo', { cookieJar }); + const { value: state } = getCookie('state', cookieJar, baseUrl) as Cookie; + + const decodedState = decodeState(state.split('.')[0]); + expect(decodedState).toEqual({ + returnTo: 'https://other-org.acme.com/foo' + }); }); test('should escape html in errors', async () => {
tests/utils/url-helpers.test.ts+19 −10 modified@@ -1,25 +1,34 @@ -import isSafeRedirect from '../../src/utils/url-helpers'; +import toSafeRedirect from '../../src/utils/url-helpers'; +import payloads from '../fixtures/open-redirect-payloads.json'; describe('url-helpers', () => { + const safeBaseUrl = new URL('http://www.example.com'); + describe('isSafeRedirect', () => { test('should not allow absolute urls', () => { - expect(isSafeRedirect('file://foo')).toEqual(false); - expect(isSafeRedirect('https://foo')).toEqual(false); - expect(isSafeRedirect('http://foo')).toEqual(false); + expect(toSafeRedirect('file://foo', safeBaseUrl)).toEqual(undefined); + expect(toSafeRedirect('https://foo', safeBaseUrl)).toEqual(undefined); + expect(toSafeRedirect('http://foo', safeBaseUrl)).toEqual(undefined); }); test('should allow relative urls', () => { - expect(isSafeRedirect('/foo')).toEqual(true); - expect(isSafeRedirect('/foo?some=value')).toEqual(true); + expect(toSafeRedirect('/foo', safeBaseUrl)).toEqual('http://www.example.com/foo'); + expect(toSafeRedirect('foo', safeBaseUrl)).toEqual('http://www.example.com/foo'); + expect(toSafeRedirect('/foo?some=value', safeBaseUrl)).toEqual('http://www.example.com/foo?some=value'); + expect(toSafeRedirect('/foo?someUrl=https://www.google.com', safeBaseUrl)).toEqual( + 'http://www.example.com/foo?someUrl=https://www.google.com' + ); + expect(toSafeRedirect('/foo', new URL('http://www.example.com:8888'))).toEqual('http://www.example.com:8888/foo'); }); test('should prevent open redirects', () => { - expect(isSafeRedirect('//google.com')).toEqual(false); - expect(isSafeRedirect('///google.com')).toEqual(false); + for (const payload of payloads) { + expect(toSafeRedirect(payload, safeBaseUrl) || safeBaseUrl.toString()).toMatch(/^http:\/\/www.example.com\//); + } }); - test('should throw when non string provided', () => { - expect(isSafeRedirect.bind(null)).toThrow('Invalid url: undefined'); + test('should not throw for empty redirect', () => { + expect(toSafeRedirect.bind(null, '', safeBaseUrl)).not.toThrow(); }); }); });
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- github.com/advisories/GHSA-2mqv-4j3r-vjvpghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2021-43812ghsaADVISORY
- github.com/auth0/nextjs-auth0/commit/0bbd9f8a0c93af51f607f28633b5fb18c5e48ad6ghsax_refsource_MISCWEB
- github.com/auth0/nextjs-auth0/security/advisories/GHSA-2mqv-4j3r-vjvpghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.