CVE-2026-59276
This Vulnerability has been fixed in the Never-Ending Support (NES) version offered by HeroDevs.
Overview
Spring Security is the authentication and access-control framework for Spring-based applications. It provides the servlet filter chain that authenticates incoming HTTP requests, the password encoding and token services that protect stored credentials, and the authorization model that decides which principal may reach which resource.
An Information Exposure vulnerability (CVE-2026-59276) has been identified in several Spring Security components that compare security-sensitive values with ordinary string equality, including the HTTP Digest authentication filter and the key-based persistence token service, which allows attackers to recover secret values one character at a time by measuring how long the server takes to reject an incorrect guess.
MITRE's CWE-208 describes this weakness as one where "Two separate operations in a product require different amounts of time to complete, in a way that is observable to an actor and reveals security-relevant information about the state of the product, such as whether a particular operation was successful or not."
This issue affects the Digest authentication filter, the key-based persistence token service, the password4j password encoders and the in-memory OAuth2 authorization service of Spring Security. The password4j encoders and the in-memory OAuth2 authorization service ship in the Spring Security release train only from 7.0 onward, so on the 6.5.x line and below the Digest authentication filter and the key-based persistence token service are the only affected components.
Details
Module Info
- Product: Spring Security
- Affected packages:
org.springframework.security:spring-security-web,org.springframework.security:spring-security-core - Affected versions:
org.springframework.security:spring-security-web: >=7.1.0 <7.1.1, >=7.0.0 <7.0.7, >=6.5.0 <=6.5.11, >=6.4.0 <=6.4.18, >=6.0.0 <=6.3.10, >=5.8.0 <=5.8.27, >=5.7.0 <=5.7.25, <=5.6.12org.springframework.security:spring-security-core: >=7.1.0 <7.1.1, >=7.0.0 <7.0.7, >=6.5.0 <=6.5.11, >=6.4.0 <=6.4.18, >=6.0.0 <=6.3.10, >=5.8.0 <=5.8.27, >=5.7.0 <=5.7.25, <=5.6.12- GitHub repository: https://github.com/spring-projects/spring-security
- Published packages: https://central.sonatype.com/artifact/org.springframework.security/spring-security-web, https://central.sonatype.com/artifact/org.springframework.security/spring-security-core
- Package manager: Maven
- Fixed in:
- NES for Spring Security: v4.2.32, v5.5.13, v5.7.26, v5.8.29, v6.2.17, v6.3.15, v6.4.18, and v6.5.13
- OSS Spring Security: 7.1.1 and 7.0.7
Vulnerability Info
This Medium-severity vulnerability is found in the org.springframework.security:spring-security-web and org.springframework.security:spring-security-core packages in the HTTP Digest authentication filter and the key-based persistence token service of Spring Security. The same non-constant-time comparison also appears in the password4j password encoders and in the in-memory OAuth2 authorization service, which the Spring Security release train carries only from 7.0 onward.
DigestAuthenticationFilter compares the digest that the server computes from the stored password against the response value supplied in the client's Authorization: Digest header using String.equals:
serverDigestMd5 = digestAuth.calculateServerDigest(user.getPassword(), request.getMethod());
// If digest is incorrect, try refreshing from backend and recomputing
if (!serverDigestMd5.equals(digestAuth.getResponse()) && cacheWasUsed) {
logger.debug("Digest comparison failure; trying to refresh user from DAO in case password had changed");
user = this.userDetailsService.loadUserByUsername(digestAuth.getUsername());
this.userCache.putUserInCache(user);
serverDigestMd5 = digestAuth.calculateServerDigest(user.getPassword(), request.getMethod());
}
...
// If digest is still incorrect, definitely reject authentication attempt
if (!serverDigestMd5.equals(digestAuth.getResponse())) {
logger.debug(LogMessage.format(
"Expected response: '%s' but received: '%s'; is AuthenticationDao returning clear text passwords?",
serverDigestMd5, digestAuth.getResponse()));
String message = this.messages.getMessage("DigestAuthenticationFilter.incorrectResponse",
"Incorrect response");
fail(request, response, new BadCredentialsException(message));
return;
}
The same filter validates the signature of the client-supplied nonce the same way, so the server-side nonce key is exposed through the same channel:
// Check signature of nonce matches this expiry time
String expectedNonceSignature = DigestAuthUtils.md5Hex(this.nonceExpiryTime + ":" + entryPointKey);
if (!expectedNonceSignature.equals(nonceTokens[1])) {
throw new BadCredentialsException(DigestAuthenticationFilter.this.messages.getMessage(
"DigestAuthenticationFilter.nonceCompromised", new Object[] { nonceAsPlainText },
"Nonce token compromised {0}"));
}
KeyBasedPersistenceTokenService verifies the keyed digest carried in a token the same way when the token is presented for validation:
String sha1Hex = tokens[tokens.length - 1];
// Verification
String content = creationTime + ":" + pseudoRandomNumber + ":" + extendedInfo.toString();
String expectedSha512Hex = Sha512DigestUtils.shaHex(content + ":" + serverSecret);
Assert.isTrue(expectedSha512Hex.equals(sha1Hex), "Key verification failure");
In every case the attacker fully controls the value being compared: the response and nonce fields of the Authorization header reach the filter before any credential has been proven, and the token string reaches the token service the same way. String.equals returns as soon as it reaches the first differing character, so the time the server spends rejecting a wrong value grows with the number of leading characters that happen to match the expected value. An unauthenticated attacker who can issue a very large number of requests and measure response latency precisely can therefore learn the expected digest, nonce signature or verification key character by character, and then replay it to authenticate or to forge a valid token without ever knowing the underlying password or server secret. Exploitation requires many requests and low network jitter, which is why the impact is rated Medium rather than higher, but the leak itself is present on every request that reaches these comparisons.
Mitigation
Only recent versions of Spring Security receive community support. Older lines are End-of-Life and will not receive public updates to address this issue.
Users of the affected components should apply one of the following mitigations:
- Upgrade to a currently supported version of Spring Security.
- Leverage a commercial support partner like HeroDevs for post-EOL security support.
Credits
- Andrey Litvitski (finder)