CIS Keycloak Benchmark
Security configuration recommendations for Keycloak identity and access management server
v1.0.0 01-2025Overview
▶This benchmark provides prescriptive guidance for establishing a secure configuration posture for Keycloak IAM. It covers realm security, authentication flows, client configuration, token management, admin console protection, event logging, and transport security using the Keycloak Admin REST API and configuration files.
| Section | Area | Focus |
|---|---|---|
| 1 | Realm Security | Registration controls, password policies, and brute force protection |
| 2 | Authentication Flows | OTP/TOTP and WebAuthn multi-factor authentication |
| 3 | Client Configuration | Confidential clients, PKCE, and least-privilege scopes |
| 4 | Token Management | Token lifetime minimization and refresh token revocation |
| 5 | Admin Console Security | Admin account replacement and security header configuration |
| 6 | Event Logging | Login/admin event capture and SIEM integration |
| 7 | Transport Security | HTTPS enforcement and database TLS connections |
Profile Definitions
▶| Profile | Description | Intended Use |
|---|---|---|
| L1 | Level 1 — Standard | Essential security for all Keycloak deployments; minimal performance impact. |
| L2 | Level 2 — Hardened | Advanced hardening for PCI-DSS, HIPAA, or high-security environments. |
1 — Realm Security
▶1.1 Realm Configuration
▶This recommendation verifies that self-registration is disabled on production realms on the Keycloak identity and access management server. Disabling or removing unnecessary components reduces the attack surface and limits potential vectors for exploitation.
Running unnecessary components on the Keycloak identity and access management server increases the attack surface and the risk of exploitation. Disabling or removing them follows the principle of least functionality and reduces exposure to known vulnerabilities.
# Check realm login settings via Admin REST API:
curl -s -H "Authorization: Bearer $TOKEN" \
https://keycloak.example.com/admin/realms/master | \
jq '{registrationAllowed, resetPasswordAllowed, loginWithEmailAllowed, duplicateEmailsAllowed}'# Disable self-registration on production realms:
curl -s -X PUT -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
https://keycloak.example.com/admin/realms/production \
-d '{
"registrationAllowed": false,
"resetPasswordAllowed": true,
"loginWithEmailAllowed": true,
"duplicateEmailsAllowed": false,
"verifyEmail": true
}'This recommendation verifies that a strong password policy is defined on the Keycloak identity and access management server. Implementing this control strengthens the overall security configuration and reduces exposure to potential threats.
Failure to implement this control may leave the Keycloak identity and access management server vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
# Check password policy: curl -s -H "Authorization: Bearer $TOKEN" \ https://keycloak.example.com/admin/realms/production | \ jq '.passwordPolicy'
# Set strong password policy:
curl -s -X PUT -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
https://keycloak.example.com/admin/realms/production \
-d '{
"passwordPolicy": "length(12) and upperCase(1) and lowerCase(1) and digits(1) and specialChars(1) and notUsername and passwordHistory(5) and forceExpiredPasswordChange(90)"
}'This recommendation verifies that brute force detection is enabled on the Keycloak identity and access management server. Implementing this control strengthens the overall security configuration and reduces exposure to potential threats.
Failure to implement this control may leave the Keycloak identity and access management server vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
# Check brute force protection:
curl -s -H "Authorization: Bearer $TOKEN" \
https://keycloak.example.com/admin/realms/production | \
jq '{bruteForceProtected, permanentLockout, maxFailureWaitSeconds, failureFactor, waitIncrementSeconds}'# Enable brute force detection:
curl -s -X PUT -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
https://keycloak.example.com/admin/realms/production \
-d '{
"bruteForceProtected": true,
"permanentLockout": false,
"failureFactor": 5,
"waitIncrementSeconds": 60,
"maxFailureWaitSeconds": 900,
"maxDeltaTimeSeconds": 43200,
"quickLoginCheckMilliSeconds": 1000,
"minimumQuickLoginWaitSeconds": 60
}'2 — Authentication Flows
▶2.1 Multi-Factor Authentication
▶This recommendation verifies that OTP/TOTP is configured in authentication flows on the Keycloak identity and access management server. Implementing this control strengthens the overall security configuration and reduces exposure to potential threats.
Failure to implement this control may leave the Keycloak identity and access management server vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
# List authentication flows: curl -s -H "Authorization: Bearer $TOKEN" \ https://keycloak.example.com/admin/realms/production/authentication/flows | \ jq '.[].alias' # Check OTP policy: curl -s -H "Authorization: Bearer $TOKEN" \ https://keycloak.example.com/admin/realms/production | \ jq '.otpPolicyType, .otpPolicyAlgorithm, .otpPolicyDigits'
# Configure OTP policy:
curl -s -X PUT -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
https://keycloak.example.com/admin/realms/production \
-d '{
"otpPolicyType": "totp",
"otpPolicyAlgorithm": "HmacSHA256",
"otpPolicyDigits": 6,
"otpPolicyPeriod": 30,
"otpPolicyInitialCounter": 0
}'
# Set browser flow to require OTP:
# Realm Settings > Authentication > Browser Flow
# Add OTP Form as REQUIRED executionThis recommendation verifies that WebAuthn is available for strong authentication on the Keycloak identity and access management server. Implementing this control strengthens the overall security configuration and reduces exposure to potential threats.
Failure to implement this control may leave the Keycloak identity and access management server vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
# Check WebAuthn policy:
curl -s -H "Authorization: Bearer $TOKEN" \
https://keycloak.example.com/admin/realms/production | \
jq '{webAuthnPolicyRpEntityName, webAuthnPolicyAttestationConveyancePreference, webAuthnPolicyUserVerificationRequirement}'# Configure WebAuthn:
curl -s -X PUT -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
https://keycloak.example.com/admin/realms/production \
-d '{
"webAuthnPolicyRpEntityName": "Keycloak",
"webAuthnPolicySignatureAlgorithms": ["ES256"],
"webAuthnPolicyAttestationConveyancePreference": "direct",
"webAuthnPolicyAuthenticatorAttachment": "cross-platform",
"webAuthnPolicyUserVerificationRequirement": "required",
"webAuthnPolicyCreateTimeout": 60
}'3 — Client Configuration
▶3.1 Client Security
▶This recommendation verifies that clients use confidential access type with PKCE on the Keycloak identity and access management server. Implementing this control strengthens the overall security configuration and reduces exposure to potential threats.
Failure to implement this control may leave the Keycloak identity and access management server vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
# List clients with their settings:
curl -s -H "Authorization: Bearer $TOKEN" \
https://keycloak.example.com/admin/realms/production/clients | \
jq '.[] | {clientId, publicClient, directAccessGrantsEnabled, serviceAccountsEnabled}'# Configure client securely:
curl -s -X PUT -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
https://keycloak.example.com/admin/realms/production/clients/CLIENT_UUID \
-d '{
"publicClient": false,
"directAccessGrantsEnabled": false,
"implicitFlowEnabled": false,
"standardFlowEnabled": true,
"serviceAccountsEnabled": false,
"consentRequired": true,
"fullScopeAllowed": false
}'This recommendation verifies that client scopes follow least privilege on the Keycloak identity and access management server. Implementing this control strengthens the overall security configuration and reduces exposure to potential threats.
Failure to implement this control may leave the Keycloak identity and access management server vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
# Check client scopes: curl -s -H "Authorization: Bearer $TOKEN" \ https://keycloak.example.com/admin/realms/production/client-scopes | \ jq '.[].name' # Check default scopes for a client: curl -s -H "Authorization: Bearer $TOKEN" \ https://keycloak.example.com/admin/realms/production/clients/CLIENT_UUID/default-client-scopes | \ jq '.[].name'
# Remove unnecessary default scopes:
curl -s -X DELETE -H "Authorization: Bearer $TOKEN" \
https://keycloak.example.com/admin/realms/production/clients/CLIENT_UUID/default-client-scopes/SCOPE_UUID
# Create minimal scope:
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
https://keycloak.example.com/admin/realms/production/client-scopes \
-d '{
"name": "minimal-profile",
"protocol": "openid-connect",
"attributes": {"include.in.token.scope": "true"},
"protocolMappers": [{
"name": "sub",
"protocol": "openid-connect",
"protocolMapper": "oidc-sub-mapper"
}]
}'4 — Token Management
▶4.1 Token Lifetimes & Revocation
▶This setting ensures that token lifetimes are minimized on the Keycloak identity and access management server. Restricting this capability limits potential abuse and enforces the principle of least privilege across the environment.
Unrestricted access to this capability could allow unauthorized users or processes to perform actions beyond their intended scope. Applying least-privilege principles to the Keycloak identity and access management server is essential for defense in depth.
# Check token lifetimes:
curl -s -H "Authorization: Bearer $TOKEN" \
https://keycloak.example.com/admin/realms/production | \
jq '{accessTokenLifespan, ssoSessionIdleTimeout, ssoSessionMaxLifespan, accessCodeLifespan}'# Set conservative token lifetimes:
curl -s -X PUT -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
https://keycloak.example.com/admin/realms/production \
-d '{
"accessTokenLifespan": 300,
"accessTokenLifespanForImplicitFlow": 300,
"ssoSessionIdleTimeout": 1800,
"ssoSessionMaxLifespan": 36000,
"offlineSessionIdleTimeout": 2592000,
"accessCodeLifespan": 60,
"accessCodeLifespanLogin": 1800,
"accessCodeLifespanUserAction": 300
}'This recommendation verifies that refresh token revocation is enabled on the Keycloak identity and access management server. Implementing this control strengthens the overall security configuration and reduces exposure to potential threats.
Failure to implement this control may leave the Keycloak identity and access management server vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
# Check refresh token settings:
curl -s -H "Authorization: Bearer $TOKEN" \
https://keycloak.example.com/admin/realms/production | \
jq '{revokeRefreshToken, refreshTokenMaxReuse, ssoSessionIdleTimeoutRememberMe}'# Enable refresh token revocation:
curl -s -X PUT -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
https://keycloak.example.com/admin/realms/production \
-d '{
"revokeRefreshToken": true,
"refreshTokenMaxReuse": 0
}'5 — Admin Console Security
▶5.1 Administrative Access
▶This recommendation verifies that default admin account is replaced on the Keycloak identity and access management server. Implementing this control strengthens the overall security configuration and reduces exposure to potential threats.
Failure to implement this control may leave the Keycloak identity and access management server vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
# Check admin console access: curl -s -H "Authorization: Bearer $TOKEN" \ https://keycloak.example.com/admin/realms/master/users | \ jq '.[].username' # Check admin roles: curl -s -H "Authorization: Bearer $TOKEN" \ https://keycloak.example.com/admin/realms/master/roles | \ jq '.[].name'
# Create dedicated admin user and disable default admin:
# 1. Create realm-admin role:
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
https://keycloak.example.com/admin/realms/master/users \
-d '{
"username": "keycloak-admin",
"enabled": true,
"emailVerified": true,
"credentials": [{"type": "password", "value": "CHANGE-ME", "temporary": true}]
}'
# 2. Assign admin role to new user
# 3. Disable default 'admin' userThis recommendation verifies that security headers are configured on the Keycloak identity and access management server. Implementing this control strengthens the overall security configuration and reduces exposure to potential threats.
Failure to implement this control may leave the Keycloak identity and access management server vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
# Check realm security headers: curl -sI https://keycloak.example.com/realms/production/.well-known/openid-configuration | \ grep -iE 'x-frame|x-content|strict-transport|content-security'
# Configure security headers in Keycloak: # Realm Settings > Security Defenses > Headers # X-Frame-Options: SAMEORIGIN # Content-Security-Policy: frame-src 'self'; frame-ancestors 'self' # X-Content-Type-Options: nosniff # X-XSS-Protection: 1; mode=block # Strict-Transport-Security: max-age=31536000; includeSubDomains # Or via reverse proxy (nginx): # add_header X-Frame-Options SAMEORIGIN; # add_header X-Content-Type-Options nosniff; # add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains';
6 — Event Logging
▶6.1 Audit & Monitoring
▶This recommendation verifies that login and admin events are logged on the Keycloak identity and access management server. Implementing this control strengthens the overall security configuration and reduces exposure to potential threats.
Failure to implement this control may leave the Keycloak identity and access management server vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
# Check events configuration: curl -s -H "Authorization: Bearer $TOKEN" \ https://keycloak.example.com/admin/realms/production/events/config | jq .
# Enable event logging:
curl -s -X PUT -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
https://keycloak.example.com/admin/realms/production/events/config \
-d '{
"eventsEnabled": true,
"eventsExpiration": 7776000,
"eventsListeners": ["jboss-logging"],
"enabledEventTypes": [
"LOGIN", "LOGIN_ERROR", "LOGOUT", "REGISTER",
"CODE_TO_TOKEN", "CODE_TO_TOKEN_ERROR",
"CLIENT_LOGIN", "CLIENT_LOGIN_ERROR",
"FEDERATED_IDENTITY_LINK", "REMOVE_FEDERATED_IDENTITY",
"UPDATE_EMAIL", "UPDATE_PROFILE",
"GRANT_CONSENT", "REVOKE_GRANT"
],
"adminEventsEnabled": true,
"adminEventsDetailsEnabled": true
}'This recommendation verifies that structured logging is forwarded to SIEM on the Keycloak identity and access management server. Implementing this control strengthens the overall security configuration and reduces exposure to potential threats.
Failure to implement this control may leave the Keycloak identity and access management server vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
# Check Keycloak log configuration: cat /opt/keycloak/conf/keycloak.conf 2>/dev/null | grep -i log # Check Quarkus logging: grep -r 'quarkus.log' /opt/keycloak/conf/ 2>/dev/null
# Configure structured logging:
# /opt/keycloak/conf/keycloak.conf:
log=console,file
log-file=/var/log/keycloak/keycloak.log
log-file-format=%d{yyyy-MM-dd HH:mm:ss} %-5p [%c] %s%e%n
log-level=INFO
# Forward to SIEM:
# /etc/filebeat/conf.d/keycloak.yml:
- type: log
paths:
- /var/log/keycloak/keycloak.log
fields:
app: keycloak
multiline.pattern: '^[0-9]{4}-'
multiline.negate: true
multiline.match: after7 — Transport Security
▶7.1 TLS & Database Security
▶This recommendation ensures that HTTPS is enforced with strict hostname on the Keycloak identity and access management server. Enforcing this requirement establishes a minimum security standard and prevents insecure configurations.
Without this enforcement, the Keycloak identity and access management server may accept insecure configurations that weaken overall security posture. Mandating this control ensures consistent protection against common attack vectors.
# Check HTTPS configuration: curl -sI https://keycloak.example.com/ | head -5 # Check hostname settings: cat /opt/keycloak/conf/keycloak.conf | grep -E 'hostname|https'
# Configure HTTPS: # /opt/keycloak/conf/keycloak.conf: hostname=keycloak.example.com hostname-strict=true hostname-strict-https=true https-certificate-file=/etc/keycloak/tls/tls.crt https-certificate-key-file=/etc/keycloak/tls/tls.key http-enabled=false # Start with production profile: /opt/keycloak/bin/kc.sh start --optimized
This recommendation verifies that database connection uses TLS on the Keycloak identity and access management server. Implementing this control strengthens the overall security configuration and reduces exposure to potential threats.
Failure to implement this control may leave the Keycloak identity and access management server vulnerable to attack or non-compliant with organizational security policies. This control helps establish a consistent, hardened configuration baseline.
# Check database TLS: cat /opt/keycloak/conf/keycloak.conf | grep -E '^db'
# Configure database connection with TLS:
# /opt/keycloak/conf/keycloak.conf:
db=postgres
db-url=jdbc:postgresql://db.example.com:5432/keycloak?sslmode=verify-full&sslrootcert=/etc/keycloak/db-ca.crt
db-username=keycloak
db-password=${KC_DB_PASSWORD}
# Encrypt sensitive configuration:
# Use Vault or environment variables for secrets:
# export KC_DB_PASSWORD='secure-password'
# export KC_HTTPS_KEY_STORE_PASSWORD='keystore-pass'