Skip to main content

Securing Keycloak in Production

Securing Keycloak in production means closing every path a login can take, not only the login page your users see. Keycloak ships the controls for most attacks against an identity provider, but many of them are off by default, a few only work in combination, and some attacks are outside what any identity server can stop on its own. This page is the map: each common attack on a public Keycloak realm, what Keycloak does about it by default, what you have to configure, where you need another tool, and the page that goes deeper.

It draws on incidents we have worked on with customers. Behavior described in this section was reproduced on Keycloak 26.8.1.

What attacks on a public realm look like​

Once a realm is reachable from the internet, automated scanners find it, usually within days. What follows is rarely a vulnerability in Keycloak. It is the realm's own defaults, used at scale:

  • Scripts, not browsers. Attack traffic posts straight to the login and token endpoints and never renders a page, so it never runs JavaScript and never produces a CAPTCHA token. A control that only works in the browser is a control the attacker never sees.
  • Leaked passwords, one try per account. Most campaigns replay email and password pairs leaked from other sites, from a pool of addresses that changes every few attempts. Per-user lockout and per-IP rate limits each see too little of it to trigger.
  • Every way in, in turn. Every realm has the same built-in clients, so the attacker knows their IDs in advance. When one path is closed, the same campaign shows up on the next within hours: an application client, then the password grant on admin-cli, then account-console, then security-admin-console. A realm is only as protected as its least protected client.
  • Failures are loud, takeovers are quiet. Failed logins flood the log. The logins that worked do not appear in it at all by default, and only the event store shows them.

Threats, defenses and gaps​

The core of this section. For each threat: what Keycloak gives you, what you have to turn on, and what it cannot do.

ThreatKeycloak by defaultWhat you configureWhere you need another toolGo deeper
Credential stuffingSame error for unknown user and wrong passwordReject the password grant with a client policy, a CAPTCHA that fails closed on every browser flow, brute force detection, event storageBot management at the edge for very large campaignsCredential Stuffing and Account Takeover
Password sprayingNothing; brute force detection is offBrute force detection, a CAPTCHA, an interim gate on the direct grant flowDistributed-attack detection across accountsPassword Spraying
Brute force against one accountNothing; off by defaultBrute force detection, and a plan for lockout being used against you—Brute Force Detection
Layer-7 login floodsEvery login costs a full password hashAn HTTP queue limit, a CAPTCHA checked before the password, sizingA WAF with rate rules; bot managementLayer-7 Floods
Volumetric DDoSNothing an application server can do—A CDN or DDoS scrubbing service in front of the clusterLayer-7 Floods
Email bombing and bot signupsNo throttle on password reset: in testing, 100 requests sent 100 emails in under five seconds. Registration is open to scripts once enabledA CAPTCHA before every step that sends email, CAPTCHA on registration, self-registration off if unusedBot management; your email provider's rate limitsEmail Abuse
Username enumerationLogin gives the same response and timing for known and unknown users. Registration says when a username or email existsReset and registration settings, a username-first flow with care—Username Enumeration
Phishing proxies (adversary in the middle)TOTP and email codes can be relayedPasskeys or WebAuthn, short SSO idle times, re-authentication for sensitive actionsLookalike-domain monitoring and takedownPhishing-Resistant Login
Breached passwordsPolicies are checked only when a password is setA password blacklist, refreshed on a scheduleA breached-password service at registration and password changeCredential Stuffing
Stolen tokens and session cookiesFive-minute access tokens. Refresh token rotation is off, and logout does not recall an access token already issuedLifespans, refresh token rotation, DPoP, offline access only where needed—Tokens and Sessions
OAuth protocol attacksiss in every authorization response and framing headers on. Wildcard redirect URIs, PKCE plain and refresh token reuse are all allowedClient policies for PKCE, no implicit or password grants, client authentication—RFC 9700 for Keycloak
Admin console exposure/admin is served on the same hostname as loginRestrict /admin paths by network, disable unused built-in clientsA proxy or WAF rule if you self-hostSign-in Methods, Grant Types and Clients
Clickjacking and framingX-Frame-Options: SAMEORIGIN and a CSP with frame-ancestors 'self'Keep them under Realm Settings → Security Defenses; widen only for origins you control—RFC 9700 for Keycloak
Undetected takeoversEvents are not saved, and the log omits successful loginsEvent storage with a long enough retention, login metricsA SIEM or anomaly detectionDetecting Attacks
Known vulnerabilitiesUpstream security advisories with each releaseUpgrade on a schedule and track advisories—Keycloak release notes

Which login paths a CAPTCHA can protect​

A CAPTCHA only protects a request that comes from a rendered page. That rules it out for several of the paths an attacker will use, which is why rejecting or gating those paths matters as much as the CAPTCHA itself.

Login pathCan a CAPTCHA apply?The control that fits
Browser username and passwordYes, checked before the passwordA CAPTCHA that fails closed, bound to the realm's default browser flow so built-in clients get it too
Username-first loginYes, on the username stepThe same, which also slows enumeration
RegistrationYesA registration CAPTCHA
Reset password, magic link, email codesYes, as a step before the email is sentA standalone CAPTCHA step
account-console, security-admin-consoleOnly if the CAPTCHA is in the realm's default browser flowDisable unused clients, restrict /admin
Password grant (grant_type=password)No. No page is renderedReject it with a client policy, or gate the direct grant flow until you can
Client credentials, token exchange, CIBANo. No user password is involvedConfidential clients with strong client authentication, client policies
RefreshNot needed; the request is already bound to a sessionRefresh token rotation

Edge rules can help with the browser paths, which carry client_id in the query string. On the token endpoint, client_id is in the POST body, which most edge rules cannot read, so blocking the password grant for one client at the edge does not work. Do it in Keycloak.

What to do before go-live, in order​

  1. Know your entry points. List every client and which grant types it allows. Sign-in Methods, Grant Types and Clients maps which protections apply to which path, and Which Keycloak clients should be enabled when you go live? covers the built-in ones.
  2. Reject the password grant realm-wide with a client policy, after moving backend services to service accounts. If a service still depends on it, gate the direct grant flow in the meantime.
  3. Put a CAPTCHA that fails closed in the realm's default browser flow, before the password, and before every step that sends email. See Cloudflare Turnstile, and test it with a request that carries no token.
  4. Turn on brute force detection and save login and lockout events with a retention longer than the time it would take you to notice an attack.
  5. Set token and session lifespans deliberately, with refresh token rotation and offline access only for the clients that need it. See Tokens and Sessions.
  6. Offer passkeys, which are the factor that holds up against phishing proxies. See Passkeys.
  7. Restrict the admin paths to your own networks.
  8. Decide where events go and who gets alerted. See Detecting Attacks.

The full pre-launch list is the Go-Live Checklist. For the server-level half of a self-hosted deployment (hostname, proxy headers, TLS, probes, clustering), see the Keycloak production readiness checklist.

Where Keycloak stops​

Keycloak is an identity server, not a network edge or a security operations tool. Plan for these separately:

  • Volumetric DDoS protection. Traffic that saturates the network or the load balancer never reaches Keycloak in a form it can refuse. Use a CDN or scrubbing service.
  • Bot management. Telling a scripted browser from a person across many requests, beyond a CAPTCHA on one form, is an edge product.
  • Breached-credential intelligence. Keycloak cannot know which passwords leaked elsewhere after they were set.
  • Detection and response. Keycloak records events; it does not correlate them, score risk, or alert. Ship events to a SIEM or a monitoring stack that does.
  • Lookalike domains and phishing. Passkeys make a phishing proxy fail at login, but finding and taking down the lookalike domain is outside the identity server.

On a Phase Two hosted cluster​

We provideYou configure
A web application firewall in front of your cluster with per-IP rate limits by tierRejecting the password grant with a client policy
IP restrictions for /admin paths on Premium and Enterprise, and for public paths on EnterpriseA CAPTCHA in your browser flows. The Cloudflare Turnstile authenticators are included in every cluster and are off until you configure them
Event capture into the Phase Two audit log, independent of your realm's event settings, plus metrics and logs in the dashboardBrute force detection, your password policy and password blacklist
A master realm that is not exposed to customers, and the server, proxy and TLS configurationWhich built-in clients stay on. Keep security-admin-console enabled on a hosted cluster; the dashboard's Console link signs in through it
A SOC 2 Type II and ISO 27001 certified operationToken and session lifespans, and passkeys for your users
Help during an incident: email support@phasetwo.ioRemediating affected accounts and notifying users

Guides​