Chuyển đến nội dung chính

Bài 19: Bảo mật nâng cao và Vault Integration

Security hardening Keycloak, Content Security Policy headers, brute-force detection configuration, password policies (chi tiết từng policy), session management (Session Limits, Idle/Max timeout), CORS configuration, clickjacking protection, HTTPS/TLS best practices, certificate management, Vault integration (HashiCorp Vault, Kubernetes Secrets, file-based), rotating credentials, Admin Console access restrictions.

🔒 DevSecOps — Bài 19 Bài 19: Bảo mật nâng cao và Vault Integration

Keycloak từ Cơ bản đến Nâng cao

Phần 5: Themes, Events, Security và Vault

xdev.asia

1. Security Hardening Checklist

Trước khi đưa Keycloak lên production, cần thực hiện đầy đủ các biện pháp hardening sau:

#Hạng mụcMức độTrạng thái
1HTTPS/TLS enforcementCritical☐
2Đổi Admin credentials mặc địnhCritical☐
3Brute-force detectionHigh☐
4Password policiesHigh☐
5Session timeoutsHigh☐
6CSP headersHigh☐
7Clickjacking protectionHigh☐
8CORS configurationMedium☐
9Vault integration cho secretsHigh☐
10Admin Console access restrictionHigh☐
11Disable unused features/endpointsMedium☐
12Database encryption at restHigh☐
13Token security (short-lived)High☐
14Audit logging enabledHigh☐

2. Content Security Policy (CSP) Headers

CSP headers bảo vệ chống XSS attacks bằng cách kiểm soát resources nào được phép load trên page.

2.1 Cấu hình CSP trong Keycloak

Keycloak cấu hình CSP thông qua Realm Settings → Security Defenses:

# Headers tab → Content-Security-Policy
frame-src 'self'; frame-ancestors 'self'; object-src 'none';

# Nâng cao: restrict thêm script-src, style-src
frame-src 'self'; frame-ancestors 'self'; object-src 'none'; \
script-src 'self' 'unsafe-inline'; \
style-src 'self' 'unsafe-inline';

2.2 Cấu hình qua REST API

# Cập nhật Security Headers cho realm
curl -X PUT "http://localhost:8080/admin/realms/my-realm" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "browserSecurityHeaders": {
        "contentSecurityPolicy": "frame-src '\''self'\''; frame-ancestors '\''self'\''; object-src '\''none'\'';",
        "contentSecurityPolicyReportOnly": "",
        "xContentTypeOptions": "nosniff",
        "xRobotsTag": "none",
        "xFrameOptions": "SAMEORIGIN",
        "strictTransportSecurity": "max-age=31536000; includeSubDomains",
        "xXSSProtection": "1; mode=block",
        "referrerPolicy": "no-referrer"
    }
  }'

2.3 Các header bảo mật quan trọng

HeaderGiá trị khuyên dùngMục đích
X-Frame-OptionsSAMEORIGINChống clickjacking
X-Content-Type-OptionsnosniffChống MIME type sniffing
X-XSS-Protection1; mode=blockBật XSS filter trình duyệt
Strict-Transport-Securitymax-age=31536000; includeSubDomainsForce HTTPS (HSTS)
Referrer-Policyno-referrerKhông gửi referrer header
Content-Security-PolicyRestrict frame-src, object-srcChống XSS, data injection

3. Brute-force Detection

Keycloak tích hợp sẵn cơ chế chống brute-force cho login attempts.

3.1 Cấu hình Brute-force

Vào Realm Settings → Security Defenses → Brute Force Detection:

# Bật/tắt
Enabled: ON

# Lockout settings
Permanent Lockout: OFF                  # Có khóa vĩnh viễn không
Max Login Failures: 5                   # Số lần thất bại tối đa trước khi lock
Wait Increment (seconds): 60            # Thời gian chờ tăng dần mỗi lần fail tiếp
Max Wait (seconds): 900                 # Thời gian chờ tối đa (15 phút)
Quick Login Check (milliseconds): 1000  # Khoảng thời gian giữa 2 lần login nhanh
Minimum Quick Login Wait: 60            # Chờ tối thiểu khi quick login detected
Failure Reset Time (seconds): 43200     # Reset failure counter sau 12 giờ

3.2 Cấu hình qua REST API

curl -X PUT "http://localhost:8080/admin/realms/my-realm" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "bruteForceProtected": true,
    "permanentLockout": false,
    "maxFailureWaitSeconds": 900,
    "minimumQuickLoginWaitSeconds": 60,
    "waitIncrementSeconds": 60,
    "quickLoginCheckMilliSeconds": 1000,
    "maxDeltaTimeSeconds": 43200,
    "failureFactor": 5
  }'

3.3 Cách hoạt động Brute-force Detection

Lần fail thứ 1: Không lockout
Lần fail thứ 2: Không lockout
Lần fail thứ 3: Không lockout
Lần fail thứ 4: Không lockout
Lần fail thứ 5: Lockout 60 giây (waitIncrementSeconds)
Lần fail thứ 6: Lockout 120 giây (60 * 2)
Lần fail thứ 7: Lockout 240 giây (60 * 4)
...tiếp tục tăng...
Max lockout: 900 giây (15 phút)

Sau 12 giờ không fail: Reset counter về 0

3.4 Unlock User bị khóa

# Kiểm tra trạng thái brute-force cho user
curl -s "http://localhost:8080/admin/realms/my-realm/attack-detection/brute-force/users/$USER_ID" \
  -H "Authorization: Bearer $ACCESS_TOKEN" | jq .

# Response mẫu:
# {
#   "numFailures": 6,
#   "disabled": true,
#   "lastIPFailure": "192.168.1.100",
#   "lastFailure": 1710489045000
# }

# Unlock user cụ thể
curl -X DELETE "http://localhost:8080/admin/realms/my-realm/attack-detection/brute-force/users/$USER_ID" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

# Unlock tất cả users
curl -X DELETE "http://localhost:8080/admin/realms/my-realm/attack-detection/brute-force/users" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

4. Password Policies

Keycloak hỗ trợ nhiều password policies để đảm bảo mật khẩu mạnh.

4.1 Cấu hình Password Policies

Vào Authentication → Policies → Password Policy và thêm các policies:

PolicyMô tảGiá trị mẫu
lengthĐộ dài tối thiểu12
maxLengthĐộ dài tối đa128
digitsSố ký tự số tối thiểu1
lowerCaseSố ký tự thường tối thiểu1
upperCaseSố ký tự hoa tối thiểu1
specialCharsSố ký tự đặc biệt tối thiểu1
notUsernameMật khẩu không được trùng username(no value)
notEmailMật khẩu không được trùng email(no value)
passwordHistoryKhông dùng lại N mật khẩu gần nhất5
hashAlgorithmThuật toán hash passwordpbkdf2-sha512
hashIterationsSố vòng lặp hash210000
forceExpiredPasswordChangeBuộc đổi MK sau N ngày90
regulxExpressionRegex pattern tùy chỉnh^(?!.*(.)\1{2}).*$

4.2 Cấu hình qua REST API

# Set password policies cho realm
curl -X PUT "http://localhost:8080/admin/realms/my-realm" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "passwordPolicy": "length(12) and digits(1) and lowerCase(1) and upperCase(1) and specialChars(1) and notUsername and notEmail and passwordHistory(5) and hashAlgorithm(pbkdf2-sha512) and hashIterations(210000) and forceExpiredPasswordChange(90) and maxLength(128)"
  }'

4.3 Hash Algorithm Recommendations

AlgorithmIterations (khuyên dùng)Ghi chú
pbkdf2-sha256600,000OWASP 2023 recommendation
pbkdf2-sha512210,000OWASP 2023 recommendation
argon2N/A (Keycloak 24+)Memory-hard, recommended cho new deployments
# Sử dụng Argon2 (Keycloak 24+)
# passwordPolicy: hashAlgorithm(argon2)
curl -X PUT "http://localhost:8080/admin/realms/my-realm" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "passwordPolicy": "length(12) and digits(1) and lowerCase(1) and upperCase(1) and specialChars(1) and hashAlgorithm(argon2)"
  }'

5. Session Management

Quản lý session chặt chẽ là yếu tố quan trọng cho bảo mật.

5.1 Session Timeouts

Cấu hình tại Realm Settings → Sessions:

SettingMô tảKhuyên dùng
SSO Session IdleThời gian idle tối đa cho SSO session30 phút
SSO Session MaxThời gian tối đa cho SSO session (bất kể activity)10 giờ
SSO Session Idle Remember MeIdle timeout khi "Remember Me" enabled7 ngày
SSO Session Max Remember MeMax lifetime khi "Remember Me" enabled30 ngày
Client Session IdleIdle timeout cho client-specific session15 phút
Client Session MaxMax lifetime cho client-specific session8 giờ
Offline Session IdleIdle timeout cho offline tokens30 ngày
Offline Session MaxMax limited lifetime cho offline sessions60 ngày

5.2 Cấu hình Sessions chi tiết

# Cấu hình session timeouts
curl -X PUT "http://localhost:8080/admin/realms/my-realm" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "ssoSessionIdleTimeout": 1800,
    "ssoSessionMaxLifespan": 36000,
    "ssoSessionIdleTimeoutRememberMe": 604800,
    "ssoSessionMaxLifespanRememberMe": 2592000,
    "clientSessionIdleTimeout": 900,
    "clientSessionMaxLifespan": 28800,
    "offlineSessionIdleTimeout": 2592000,
    "offlineSessionMaxLifespan": 5184000,
    "offlineSessionMaxLifespanEnabled": true
  }'

5.3 Session Limits

Giới hạn số session đồng thời cho mỗi user:

# Authentication → Flows → Browser Flow
# Thêm step "User Session Limits"

Session Limits Configuration:
- Max concurrent sessions per user: 3
- Behavior on breach: "Terminate oldest session"

# Hoặc cấu hình qua Authentication Flow:
# 1. Copy "Browser" flow
# 2. Thêm execution "User Session Count Limiter"  
# 3. Cấu hình max sessions

5.4 Kiểm tra và Revoke Sessions

# List sessions của user
curl -s "http://localhost:8080/admin/realms/my-realm/users/$USER_ID/sessions" \
  -H "Authorization: Bearer $ACCESS_TOKEN" | jq .

# Response mẫu:
# [
#   {
#     "id": "session-id",
#     "username": "john",
#     "userId": "user-uuid",
#     "ipAddress": "192.168.1.100",
#     "start": 1710489045,
#     "lastAccess": 1710492645,
#     "clients": { "client-uuid": "my-app" }
#   }
# ]

# Revoke session cụ thể
curl -X DELETE "http://localhost:8080/admin/realms/my-realm/sessions/$SESSION_ID" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

# Logout tất cả sessions của user
curl -X POST "http://localhost:8080/admin/realms/my-realm/users/$USER_ID/logout" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

# Logout tất cả sessions trong realm
curl -X POST "http://localhost:8080/admin/realms/my-realm/logout-all" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

6. CORS Configuration

CORS (Cross-Origin Resource Sharing) được cấu hình per client trong Keycloak.

6.1 Cấu hình CORS cho Client

# Client Settings → Web Origins

# Cho phép specific origins
https://myapp.com
https://admin.myapp.com

# Cho phép tất cả redirect URIs (sử dụng "+")
+

# KHÔNG sử dụng "*" trong production
# "*" cho phép tất cả origins — nguy hiểm!
# Cấu hình CORS qua REST API
curl -X PUT "http://localhost:8080/admin/realms/my-realm/clients/$CLIENT_UUID" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "webOrigins": [
        "https://myapp.com",
        "https://admin.myapp.com"
    ]
  }'

6.2 CORS Best Practices

  • Luôn chỉ định origins cụ thể — Không dùng wildcard *
  • Sử dụng + — Tự động derive origins từ Valid Redirect URIs
  • Tách client — Mỗi frontend app nên có client riêng với CORS config riêng
  • Test kỹ — Kiểm tra CORS response headers với browser DevTools

7. Clickjacking Protection

Clickjacking attack nhúng Keycloak login page vào iframe để đánh cắp credentials.

7.1 Cấu hình X-Frame-Options

# Realm Settings → Security Defenses → Headers

X-Frame-Options: SAMEORIGIN
# - DENY: Không cho phép iframe dưới bất kỳ điều kiện nào
# - SAMEORIGIN: Chỉ cho phép iframe từ cùng origin
# - ALLOW-FROM uri: (deprecated, dùng CSP frame-ancestors thay thế)

7.2 CSP frame-ancestors (ưu tiên hơn)

# Content-Security-Policy header
frame-ancestors 'self';

# Cho phép specific parent origins
frame-ancestors 'self' https://portal.mycompany.com;

8. HTTPS/TLS Enforcement

8.1 Cấu hình Hostname và TLS

# Production: Strict HTTPS
bin/kc.sh start \
  --hostname=auth.mycompany.com \
  --hostname-strict=true \
  --https-certificate-file=/etc/certs/tls.crt \
  --https-certificate-key-file=/etc/certs/tls.key \
  --https-port=8443 \
  --http-enabled=false

8.2 TLS với Reverse Proxy (phổ biến)

# Khi sử dụng reverse proxy (Nginx, HAProxy) terminate TLS
bin/kc.sh start \
  --hostname=auth.mycompany.com \
  --hostname-strict=true \
  --proxy-headers=xforwarded \
  --http-enabled=true \
  --http-port=8080
# Nginx reverse proxy configuration
server {
    listen 443 ssl http2;
    server_name auth.mycompany.com;

    ssl_certificate /etc/ssl/certs/auth.mycompany.com.crt;
    ssl_certificate_key /etc/ssl/private/auth.mycompany.com.key;

    # Strong TLS configuration
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;

    # HSTS
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    location / {
        proxy_pass http://keycloak:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Port $server_port;

        # WebSocket support
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Timeouts
        proxy_read_timeout 90s;
        proxy_send_timeout 90s;
    }
}

# Redirect HTTP to HTTPS
server {
    listen 80;
    server_name auth.mycompany.com;
    return 301 https://$host$request_uri;
}

8.3 Mutual TLS (mTLS)

# Bật mTLS cho client authentication
bin/kc.sh start \
  --https-certificate-file=/etc/certs/tls.crt \
  --https-certificate-key-file=/etc/certs/tls.key \
  --https-trust-store-file=/etc/certs/truststore.jks \
  --https-trust-store-password=changeit \
  --https-client-auth=request

# --https-client-auth options:
# none: không yêu cầu client cert
# request: yêu cầu nhưng không bắt buộc
# required: bắt buộc client cert

8.4 Certificate Management

# Sử dụng cert-manager trong Kubernetes
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: keycloak-tls
  namespace: keycloak
spec:
  secretName: keycloak-tls-secret
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  dnsNames:
    - auth.mycompany.com
  renewBefore: 720h  # Renew 30 ngày trước khi hết hạn
# Kubernetes Deployment sử dụng TLS secret
apiVersion: apps/v1
kind: Deployment
metadata:
  name: keycloak
spec:
  template:
    spec:
      containers:
        - name: keycloak
          image: quay.io/keycloak/keycloak:26.1
          args:
            - start
            - --hostname=auth.mycompany.com
            - --https-certificate-file=/etc/certs/tls.crt
            - --https-certificate-key-file=/etc/certs/tls.key
          volumeMounts:
            - name: tls-certs
              mountPath: /etc/certs
              readOnly: true
      volumes:
        - name: tls-certs
          secret:
            secretName: keycloak-tls-secret

9. Vault Integration

Keycloak hỗ trợ lưu trữ secrets (LDAP bind password, SMTP password, client secrets) trong external vault thay vì plaintext trong database.

9.1 HashiCorp Vault Integration

# Cấu hình Keycloak sử dụng HashiCorp Vault
bin/kc.sh start \
  --vault=hashicorp \
  --vault-hashicorp-paths=/secret/data/keycloak \
  --vault-hashicorp-address=https://vault.mycompany.com:8200 \
  --vault-hashicorp-auth-method=token \
  --vault-hashicorp-token=$VAULT_TOKEN

# Hoặc sử dụng AppRole authentication
bin/kc.sh start \
  --vault=hashicorp \
  --vault-hashicorp-paths=/secret/data/keycloak \
  --vault-hashicorp-address=https://vault.mycompany.com:8200 \
  --vault-hashicorp-auth-method=approle \
  --vault-hashicorp-approle-role-id=$ROLE_ID \
  --vault-hashicorp-approle-secret-id=$SECRET_ID

9.2 Lưu Secrets trong HashiCorp Vault

# Cấu hình Vault KV secrets engine
vault secrets enable -path=secret kv-v2

# Lưu LDAP bind password
vault kv put secret/keycloak/my-realm \
  ldap_bind_credential="LdapS3cur3P@ss!" \
  smtp_password="SmtpP@ssw0rd!" \
  my-client-secret="Cl13ntS3cr3t!"

# Cấu trúc path trong Vault:
# secret/data/keycloak/{realm-name}/{key}

9.3 Sử dụng Vault Reference trong Keycloak

Trong Keycloak, sử dụng cú pháp ${vault.key} thay vì plaintext:

# LDAP User Federation
Bind Credential: ${vault.ldap_bind_credential}

# SMTP Settings
Password: ${vault.smtp_password}

# Client Secret
Client Secret: ${vault.my-client-secret}

9.4 Kubernetes/OpenShift Secrets Vault

# Sử dụng Kubernetes Secrets làm vault
bin/kc.sh start \
  --vault=file \
  --vault-dir=/mnt/secrets
# Kubernetes Secret
apiVersion: v1
kind: Secret
metadata:
  name: keycloak-vault
  namespace: keycloak
type: Opaque
stringData:
  # Format: {realm-name}_{key}
  my-realm_ldap_bind_credential: "LdapS3cur3P@ss!"
  my-realm_smtp_password: "SmtpP@ssw0rd!"
  my-realm_my-client-secret: "Cl13ntS3cr3t!"
# Mount Secret vào Keycloak Pod
apiVersion: apps/v1
kind: Deployment
metadata:
  name: keycloak
spec:
  template:
    spec:
      containers:
        - name: keycloak
          args:
            - start
            - --vault=file
            - --vault-dir=/mnt/secrets
          volumeMounts:
            - name: vault-secrets
              mountPath: /mnt/secrets
              readOnly: true
      volumes:
        - name: vault-secrets
          secret:
            secretName: keycloak-vault

9.5 File-based Vault (Development)

# Tạo vault files cho development
mkdir -p /opt/keycloak/vault/my-realm

# Mỗi file chứa một secret (filename = key)
echo -n "LdapS3cur3P@ss!" > /opt/keycloak/vault/my-realm/ldap_bind_credential
echo -n "SmtpP@ssw0rd!" > /opt/keycloak/vault/my-realm/smtp_password

# Cấu hình Keycloak
bin/kc.sh start-dev \
  --vault=file \
  --vault-dir=/opt/keycloak/vault

9.6 Rotating Credentials

# HashiCorp Vault: Rotate LDAP password
# 1. Update secret trong Vault
vault kv put secret/keycloak/my-realm \
  ldap_bind_credential="NewLdapP@ss2026!"

# 2. Keycloak sẽ tự động lấy secret mới
# (Vault SPI cache TTL = 0 by default, mỗi lần cần sẽ fetch lại)

# Kubernetes Secrets: Update secret
kubectl create secret generic keycloak-vault \
  --from-literal=my-realm_ldap_bind_credential="NewLdapP@ss2026!" \
  --dry-run=client -o yaml | kubectl apply -f -

# Rolling restart Keycloak để pickup new secrets
kubectl rollout restart deployment/keycloak -n keycloak

10. Admin Console Access Restrictions

10.1 Dedicated Admin Realm

Sử dụng master realm chỉ cho admin, không tạo user/client business:

Best Practice:
- Master realm: Chỉ chứa admin users
- Business realms: Chứa application users/clients
- Không cho phép self-registration trên master realm
- MFA bắt buộc cho admin accounts

10.2 IP Whitelist cho Admin Console

# Nginx: Restrict Admin Console access
location /admin/ {
    # Chỉ cho phép IP nội bộ
    allow 10.0.0.0/8;
    allow 172.16.0.0/12;
    allow 192.168.0.0/16;
    deny all;

    proxy_pass http://keycloak:8080;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

# Block admin REST API từ external
location /admin/realms/ {
    allow 10.0.0.0/8;
    deny all;

    proxy_pass http://keycloak:8080;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

10.3 Kubernetes Network Policy

# NetworkPolicy: Restrict Admin API access
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: keycloak-admin-restrict
  namespace: keycloak
spec:
  podSelector:
    matchLabels:
      app: keycloak
  policyTypes:
    - Ingress
  ingress:
    # Allow user-facing traffic from ingress controller
    - from:
        - namespaceSelector:
            matchLabels:
              name: ingress-nginx
      ports:
        - port: 8080
    # Allow admin traffic only from management namespace
    - from:
        - namespaceSelector:
            matchLabels:
              name: management
      ports:
        - port: 8080

11. Token Security

11.1 Short-lived Tokens

# Cấu hình token lifespans
curl -X PUT "http://localhost:8080/admin/realms/my-realm" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "accessTokenLifespan": 300,
    "accessTokenLifespanForImplicitFlow": 900,
    "actionTokenGeneratedByUserLifespan": 300,
    "actionTokenGeneratedByAdminLifespan": 43200
  }'

# Per-client token lifespan override
curl -X PUT "http://localhost:8080/admin/realms/my-realm/clients/$CLIENT_UUID" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "attributes": {
        "access.token.lifespan": "180",
        "client.session.idle.timeout": "600",
        "client.session.max.lifespan": "3600"
    }
  }'

11.2 Token Introspection

# Introspect token (kiểm tra validity)
curl -X POST "http://localhost:8080/realms/my-realm/protocol/openid-connect/token/introspect" \
  -d "client_id=my-resource-server" \
  -d "client_secret=$CLIENT_SECRET" \
  -d "token=$ACCESS_TOKEN"

# Response
# {
#   "active": true,
#   "sub": "user-uuid",
#   "aud": "my-app",
#   "exp": 1710492645,
#   "iat": 1710489045,
#   "realm_access": { "roles": ["user"] },
#   "scope": "openid profile email"
# }

11.3 Token Revocation

# Revoke refresh token
curl -X POST "http://localhost:8080/realms/my-realm/protocol/openid-connect/revoke" \
  -d "client_id=my-app" \
  -d "client_secret=$CLIENT_SECRET" \
  -d "token=$REFRESH_TOKEN" \
  -d "token_type_hint=refresh_token"

# Revoke access token
curl -X POST "http://localhost:8080/realms/my-realm/protocol/openid-connect/revoke" \
  -d "client_id=my-app" \
  -d "client_secret=$CLIENT_SECRET" \
  -d "token=$ACCESS_TOKEN" \
  -d "token_type_hint=access_token"

12. Production Deployment — Hardened

# docker-compose.production.yml - Hardened Keycloak
services:
  keycloak:
    image: quay.io/keycloak/keycloak:26.1
    command:
      - start
      - --hostname=auth.mycompany.com
      - --hostname-strict=true
      - --proxy-headers=xforwarded
      - --http-enabled=true
      - --metrics-enabled=true
      - --health-enabled=true
      - --vault=file
      - --vault-dir=/mnt/secrets
      - --log=console
      - --log-console-output=json
    environment:
      KC_DB: postgres
      KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD_FILE: /run/secrets/db_password
      KC_CACHE: ispn
      KC_CACHE_STACK: kubernetes
    volumes:
      - ./secrets:/mnt/secrets:ro
    secrets:
      - db_password
    deploy:
      replicas: 2
      resources:
        limits:
          memory: 1G
          cpus: '1.0'
        reservations:
          memory: 512M
          cpus: '0.5'
    healthcheck:
      test: ["CMD-SHELL", "exec 3<>/dev/tcp/localhost/9000 && echo -e 'GET /health/ready HTTP/1.1\r\nHost: localhost\r\n\r\n' >&3 && cat <&3 | grep -q '\"status\":\"UP\"'"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s

secrets:
  db_password:
    file: ./secrets/db_password

13. Best Practices Tổng hợp

  • HTTPS everywhere — Không bao giờ deploy Keycloak production không có TLS. Sử dụng HSTS headers.
  • Secrets in Vault — Không lưu credentials plaintext trong DB. Sử dụng HashiCorp Vault hoặc Kubernetes Secrets.
  • Strong password policies — Minimum 12 characters, sử dụng Argon2 hoặc PBKDF2-SHA512 với iterations cao.
  • Session limits — Giới hạn số sessions đồng thời. Set idle/max timeouts phù hợp.
  • Admin access restricted — IP whitelist cho Admin Console. MFA bắt buộc cho admin accounts.
  • Short-lived tokens — Access token 5 phút, refresh token rotation enabled.
  • Brute-force protection — Bật và cấu hình phù hợp. Monitor login failure rates.
  • Regular security audit — Review Keycloak configuration định kỳ. Update lên phiên bản mới nhất.
  • Separate admin realm — Sử dụng master realm chỉ cho admin. Không mix admin và business users.
  • Monitor and alert — Kết hợp event logging (Bài 18) với security monitoring cho detection và response.