CIS Keycloak Benchmark

Security configuration recommendations for Keycloak identity and access management server

v1.0.0 01-2025

Overview

▶

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.

16Recommendations
7Sections
2Profile Levels
SectionAreaFocus
1Realm SecurityRegistration controls, password policies, and brute force protection
2Authentication FlowsOTP/TOTP and WebAuthn multi-factor authentication
3Client ConfigurationConfidential clients, PKCE, and least-privilege scopes
4Token ManagementToken lifetime minimization and refresh token revocation
5Admin Console SecurityAdmin account replacement and security header configuration
6Event LoggingLogin/admin event capture and SIEM integration
7Transport SecurityHTTPS enforcement and database TLS connections

Profile Definitions

▶
ProfileDescriptionIntended Use
L1Level 1 — StandardEssential security for all Keycloak deployments; minimal performance impact.
L2Level 2 — HardenedAdvanced hardening for PCI-DSS, HIPAA, or high-security environments.

1 — Realm Security

▶

1.1 Realm Configuration

▶
1.1.1 Ensure self-registration is disabled on production realms (Automated)
L1 Auto
Description

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.

Rationale

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.

Audit
# 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}'
Remediation
# 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
  }'
1.1.2 Ensure a strong password policy is defined (Automated)
L1 Auto
Description

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.

Rationale

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.

Audit
# Check password policy:
curl -s -H "Authorization: Bearer $TOKEN" \
  https://keycloak.example.com/admin/realms/production | \
  jq '.passwordPolicy'
Remediation
# 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)"
  }'
1.1.3 Ensure brute force detection is enabled (Automated)
L1 Auto
Description

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.

Rationale

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.

Audit
# Check brute force protection:
curl -s -H "Authorization: Bearer $TOKEN" \
  https://keycloak.example.com/admin/realms/production | \
  jq '{bruteForceProtected, permanentLockout, maxFailureWaitSeconds, failureFactor, waitIncrementSeconds}'
Remediation
# 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

▶
2.1.1 Ensure OTP/TOTP is configured in authentication flows (Automated)
L1 Auto
Description

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.

Rationale

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.

Audit
# 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'
Remediation
# 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 execution
2.1.2 Ensure WebAuthn is available for strong authentication (Automated)
L2 Auto
Description

This 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.

Rationale

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.

Audit
# Check WebAuthn policy:
curl -s -H "Authorization: Bearer $TOKEN" \
  https://keycloak.example.com/admin/realms/production | \
  jq '{webAuthnPolicyRpEntityName, webAuthnPolicyAttestationConveyancePreference, webAuthnPolicyUserVerificationRequirement}'
Remediation
# 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

▶
3.1.1 Ensure clients use confidential access type with PKCE (Automated)
L1 Auto
Description

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.

Rationale

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.

Audit
# List clients with their settings:
curl -s -H "Authorization: Bearer $TOKEN" \
  https://keycloak.example.com/admin/realms/production/clients | \
  jq '.[] | {clientId, publicClient, directAccessGrantsEnabled, serviceAccountsEnabled}'
Remediation
# 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
  }'
3.1.2 Ensure client scopes follow least privilege (Automated)
L1 Auto
Description

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.

Rationale

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.

Audit
# 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'
Remediation
# 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

▶
4.1.1 Ensure token lifetimes are minimized (Automated)
L1 Auto
Description

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.

Rationale

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.

Audit
# Check token lifetimes:
curl -s -H "Authorization: Bearer $TOKEN" \
  https://keycloak.example.com/admin/realms/production | \
  jq '{accessTokenLifespan, ssoSessionIdleTimeout, ssoSessionMaxLifespan, accessCodeLifespan}'
Remediation
# 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
  }'
4.1.2 Ensure refresh token revocation is enabled (Automated)
L1 Auto
Description

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.

Rationale

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.

Audit
# Check refresh token settings:
curl -s -H "Authorization: Bearer $TOKEN" \
  https://keycloak.example.com/admin/realms/production | \
  jq '{revokeRefreshToken, refreshTokenMaxReuse, ssoSessionIdleTimeoutRememberMe}'
Remediation
# 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

▶
5.1.1 Ensure default admin account is replaced (Manual)
L1 Manual
Description

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.

Rationale

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.

Audit
# 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'
Remediation
# 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' user
5.1.2 Ensure security headers are configured (Automated)
L1 Auto
Description

This 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.

Rationale

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.

Audit
# 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'
Remediation
# 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

▶
6.1.1 Ensure login and admin events are logged (Automated)
L1 Auto
Description

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.

Rationale

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.

Audit
# Check events configuration:
curl -s -H "Authorization: Bearer $TOKEN" \
  https://keycloak.example.com/admin/realms/production/events/config | jq .
Remediation
# 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
  }'
6.1.2 Ensure structured logging is forwarded to SIEM (Automated)
L2 Auto
Description

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.

Rationale

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.

Audit
# 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
Remediation
# 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: after

7 — Transport Security

▶

7.1 TLS & Database Security

▶
7.1.1 Ensure HTTPS is enforced with strict hostname (Automated)
L1 Auto
Description

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.

Rationale

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.

Audit
# Check HTTPS configuration:
curl -sI https://keycloak.example.com/ | head -5

# Check hostname settings:
cat /opt/keycloak/conf/keycloak.conf | grep -E 'hostname|https'
Remediation
# 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
7.1.2 Ensure database connection uses TLS (Automated)
L1 Auto
Description

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.

Rationale

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.

Audit
# Check database TLS:
cat /opt/keycloak/conf/keycloak.conf | grep -E '^db'
Remediation
# 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'