fix(console): prevent open redirect in /auth/authorize continue parameter#37809
Open
sebastiondev wants to merge 1 commit into
Open
fix(console): prevent open redirect in /auth/authorize continue parameter#37809sebastiondev wants to merge 1 commit into
sebastiondev wants to merge 1 commit into
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Issue for this PR
Closes #37807
Type of change
What does this PR do?
Fixes an open redirect (CWE-601) in the console
/auth/authorizeroute.The bug.
packages/console/app/src/routes/auth/authorize.tsreads thecontinuequery parameter and concatenates it into aURLconstructor:If
cont = "//evil.com", the WHATWG URL parser resolves this tohttps://<host>/auth/callback//evil.com— still on the console origin, so it passes the OAuth provider's callback origin check. The OAuth flow then redirects the browser back to that path with acode, and the splat handler atpackages/console/app/src/routes/auth/[...callback].tscomputes:redirect("//evil.com")emits a protocol-relativeLocation: //evil.comheader. Browsers follow this tohttps://evil.com/, so the victim — who thinks they just logged in to the legitimate console — ends up on the attacker's site. Classic phishing amplifier because the initial URL is on the real console origin.The fix. Two layers in
authorize.ts:safeContinue()rejects anycontinuethat is not a safe relative path: must start with a single/, must not start with//(protocol-relative), must not contain backslashes or control chars (some parsers treat\like/), and must not contain..segments.callbackUrl, we verify the result is still on the same origin and its pathname still starts with/auth/callback. If not, return400. This catches anything the input filter missed (defence in depth).Invalid
continuevalues fall back to an empty string, so the normal flow (callback URL =/auth/callback) still works.Nothing else is touched — the change is 26 lines added, 1 removed, all in
authorize.ts.How did you verify your code works?
devcode by hitting/auth/authorize?continue=//evil.comand confirming the resolvedcallbackUrlwashttps://<host>/auth/callback//evil.com, which after the callback stage produced aLocation: //evil.comheader (browser follows to the attacker origin).safeContinuereturns"",callbackUrlbecomes.../auth/callback, redirect target is the real console. Other rejected inputs I tried:\\evil.com,/..//evil.com,/foo\r\nLocation: evil— all now normalise to empty and go through the safe path.continue=/team/foo,continue=/settings?tab=x) still resolve tocallbackUrl = /auth/callback/team/fooetc. and pass the origin+prefix check.safeContinuepass (8/8 cases covering the reject/accept matrix above).Before submitting, I tried to disprove this: I checked whether any router-level middleware, CSRF token, or OAuth
state/redirect_uriallow-list on the provider side would block the attack. The console route has no such gate — it is intentionally an unauthenticated login entry point — and the provider only checks that the callback origin matches, which//evil.compreserves. So the redirect really does land on the attacker.Screenshots / recordings
N/A — server-side redirect header change, no UI.
Checklist
Discovered by the Sebastion AI GitHub App.