Wildcard redirect URI in Keycloak realm
ID |
keycloak_wildcard_redirect_uri |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Family |
Identity Security |
Tags |
ASVS50:v10.4.1, identity, non-reachable, oauth, security |
Description
A Keycloak realm export (realm-export.json, <realm>-realm.json, or any JSON under a keycloak/
directory) registers the OAuth / OIDC clients of the realm together with the redirect URIs each client is
allowed to send the user back to.
OWASP ASVS 5.0 V10.4.1 requires the authorization server to validate redirect URIs against a client-specific allowlist of pre-registered URIs using exact string comparison. A wildcard in that allowlist defeats the validation, since every URI matching the wildcard becomes an accepted destination.
This detector reports the two shapes that widen the destination beyond a pre-registered host:
-
the catch-all
*; -
a wildcard in the scheme or the authority, such as
https://.example.com/callbackor://app.example.com/callback.
A trailing wildcard confined to the path of a concrete host (https://app.example.com/callback/*) is the
documented Keycloak idiom and is not reported, and neither is the + placeholder, which Keycloak resolves
to the client’s configured root URL.
Security
The redirect URI is where the authorization server delivers the authorization code, and for public clients and implicit-style flows the token itself. With a wildcard registered, an attacker who can get a victim to start an authorization request can point the callback at a host of their choosing and receive credentials issued for a legitimate client.
The catch-all * accepts any host at all. A wildcard in the authority is narrower but still dangerous: any
subdomain that an attacker can register, take over, or reach through a subdomain-takeover of an abandoned
DNS record becomes a valid destination. PKCE reduces but does not remove the exposure, because the leaked
code is still bound to the legitimate client, and it offers no protection at all for tokens returned
directly in the redirect.
Realm exports are frequently committed to provision development and test environments, and those same files are then reused as the starting point for production realms, which is how a permissive development default reaches a live authorization server.
Mitigation / Fix
Register the exact callback URIs each client uses, one entry per environment:
{
"realm": "demo",
"clients": [
{
"clientId": "web-app",
"redirectUris": [
"https://app.example.com/oauth2/callback",
"https://staging.example.com/oauth2/callback"
]
}
]
}
If a client genuinely needs several paths on one host, keep the wildcard in the path only
(https://app.example.com/callback/*) so the host stays pinned. For local development, prefer a dedicated
client or realm over relaxing the production one, and rotate the client secret of any confidential client
that was exposed through a wildcard, since authorization codes may already have been delivered elsewhere.
Review webOrigins at the same time: a * there widens CORS for the same client.