| Gói / sản phẩm | Phiên bản bị ảnh hưởng | Phiên bản đã vá |
|---|---|---|
| authlib | ≥ 1.7.0, < 1.7.1< 1.6.12 | 1.7.11.6.12 |
| authlib | = 1.7.0< 1.6.12 | 1.6.12 |
An unauthenticated open redirect in Authlib's OpenIDImplicitGrant and OpenIDHybridGrant authorization endpoint lets a remote attacker cause the authorization server to issue an HTTP 302 to an attacker-chosen URL by submitting an authorization request that omits the openid scope.
OpenIDImplicitGrant.validate_authorization_request in authlib/oidc/core/grants/implicit.py:
def validate_authorization_request(self):
if not is_openid_scope(self.request.payload.scope):
raise InvalidScopeError(
"Missing 'openid' scope",
redirect_uri=self.request.payload.redirect_uri, # ← raw, unvalidated
redirect_fragment=True,
)
redirect_uri = super().validate_authorization_request()
...
OpenIDHybridGrant.validate_authorization_request in authlib/oidc/core/grants/hybrid.py shares the same pattern.
Both methods perform the openid scope presence check before delegating to super().validate_authorization_request(), which is where AuthorizationEndpointMixin.validate_authorization_redirect_uri validates the requested redirect_uri against the client's check_redirect_uri(...). The InvalidScopeError thrown by the scope check therefore carries attacker-controlled self.request.payload.redirect_uri.
OAuth2Error.__call__ in authlib/oauth2/base.py renders any error with a non-empty redirect_uri as an HTTP 302:
def __call__(self, uri=None):
if self.redirect_uri:
params = self.get_body()
loc = add_params_to_uri(self.redirect_uri, params, self.redirect_fragment)
return 302, "", [("Location", loc)]
return super().__call__(uri=uri)
A malformed authorization request that selects OpenIDImplicitGrant or OpenIDHybridGrant and omits the openid scope is therefore redirected to a fully attacker-chosen URL.
This is a variant of the issue fixed in commit 3be08468 ("fix: redirecting to unvalidated redirect_uri on UnsupportedResponseTypeError") that was missed in the OIDC Implicit and Hybrid grants.
Bạn đang dùng một trong các sản phẩm này? Quét site để biết có đang lộ phiên bản có lỗ hổng không.
Quét site miễn phíOpenIDImplicitGrant or OpenIDHybridGrant (standard OIDC Implicit or Hybrid flow support).response_type that matches either grant: id_token, id_token token, code id_token, code token, or code id_token token.scope does not contain openid.redirect_uri value.No user authentication, no consent, no valid session, no CSRF token, and — notably — no valid client_id are required. The scope check runs before any client lookup, so any client_id value (including nonexistent ones) reaches the vulnerable code path.
The following unauthenticated GET is sufficient to induce the authorization server to redirect a victim's browser to an attacker-controlled URL:
GET /oauth/authorize
?response_type=id_token
&client_id=anything
&scope=profile
&redirect_uri=https%3A%2F%2Fevil.example.com%2Fphish
&state=s&nonce=n HTTP/1.1
Host: victim-op.example
Server response:
HTTP/1.1 302 Found
Location: https://evil.example.com/phish#error=invalid_scope&error_description=Missing+%27openid%27+scope&state=s
redirect_uri that has not been validated against the client's registered URIs, even in error responses. The state parameter is echoed back, giving the attacker site a stable correlator.Any application using Authlib as an OIDC provider that registers OpenIDImplicitGrant and/or OpenIDHybridGrant — i.e. anyone supporting the Implicit flow or the Hybrid flow (response_type=code id_token, etc.) — is affected. Clients of an Authlib-based OP are not directly affected; this is a server-side issue.
Authorization servers that only register the plain AuthorizationCodeGrant (code flow, with or without PKCE and the OpenIDCode extension) are not affected by this specific variant: the code-flow grant validates redirect_uri before raising scope errors. If you were affected by the sibling issue fixed in 3be08468 (UnsupportedResponseTypeError), you should already be on 1.6.10 or later; this advisory is independent of that fix.
The attached fix-oidc-open-redirect.patch reorders each method to delegate to its super (or call validate_code_authorization_request for Hybrid) first, and then performs the openid-scope check with the validated redirect_uri variable.
# authlib/oidc/core/grants/implicit.py
def validate_authorization_request(self):
redirect_uri = super().validate_authorization_request() # runs client + redirect_uri validation
if not is_openid_scope(self.request.payload.scope):
raise InvalidScopeError(
"Missing 'openid' scope",
redirect_uri=redirect_uri, # validated
redirect_fragment=True,
)
try:
validate_nonce(self.request, self.exists_nonce, required=True)
except OAuth2Error as error:
error.redirect_uri = redirect_uri
error.redirect_fragment = True
raise error
return redirect_uri
An equivalent transform is applied to OpenIDHybridGrant.validate_authorization_request, invoking validate_code_authorization_request first and only then checking is_openid_scope.
Alternatively, inline a client = query_client(request.payload.client_id) + client.check_redirect_uri(request.payload.redirect_uri) guard before populating redirect_uri on the error — the pattern used in 3be08468.
The patch also adds regression tests analogous to test_unsupported_response_type_does_not_redirect from commit 3be08468, asserting rv.status_code == 400 and rv.headers.get("Location") is None for an unregistered redirect_uri with a non-openid scope.
No clean server-side workaround exists short of patching. Partial mitigations:
OpenIDImplicitGrant and OpenIDHybridGrant if the Implicit and Hybrid flows are not required. (RFC 9700 deprecates the Implicit flow and discourages Hybrid flows, so this is recommended anyway.)/authorize endpoint with a reverse proxy rule that rejects requests containing both a redirect_uri parameter and a scope that does not include openid when response_type matches the vulnerable set. This is fragile and not recommended as a primary control.3be08468 — prior fix for the same class of issue in UnsupportedResponseTypeError (Authlib 1.6.10)5d2e603e):
OpenIDImplicitGrant.validate_authorization_request — authlib/oidc/core/grants/implicit.pyOpenIDHybridGrant.validate_authorization_request — authlib/oidc/core/grants/hybrid.pyOAuth2Error.__call__ — authlib/oauth2/base.py (renders errors with redirect_uri as HTTP 302)AuthorizationEndpointMixin.validate_authorization_redirect_uri — authlib/oauth2/rfc6749/grants/base.py (the validation that is bypassed)