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/callback or ://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.