CVE-2026-47841
This Vulnerability has been fixed in the Never-Ending Support (NES) version offered by HeroDevs.
Overview
Spring Security is the authentication and authorization framework for Spring applications. It secures servlet and reactive web applications through a configurable filter chain, and ships support for form login, OAuth 2.0, SAML 2.0, method security, and, since the 6.4 line, WebAuthn passkey registration and authentication.
An Authorization Bypass vulnerability (CVE-2026-47841) has been identified in Spring Security's WebAuthn relying party operations, which allows attackers who have obtained a victim's authenticator to complete a WebAuthn ceremony without satisfying the user verification step, such as a PIN or a biometric, even though the relying party explicitly required it. An application is exposed when it uses WebAuthn authentication, explicitly configures user verification as required, and stores HTTP sessions in a distributed store such as Spring Session backed by Redis, JDBC, or Hazelcast. The default user verification setting of preferred is not affected.
Per OWASP: Confirmation of the user's identity, authentication, and session management is critical to protect against authentication-related attacks.
This issue affects the WebAuthn support of Spring Security.
Details
Module Info
- Product: Spring Security
- Affected packages:
- 6.5.x, 6.4.x:
org.springframework.security:spring-security-web - 7.1.x, 7.0.x:
org.springframework.security:spring-security-webauthn
- 6.5.x, 6.4.x:
- Affected versions: 7.1.0, >=7.0.0 <7.0.7, >=6.5.0 <=6.5.11, >=6.4.0 <=6.4.18
- GitHub repository: https://github.com/spring-projects/spring-security
- Published packages:
- Package manager: Maven
- Fixed in:
- NES for Spring Security: 6.5.13, 6.4.18
- OSS Spring Security 7.1.1 and 7.0.7
Vulnerability Info
This High-severity vulnerability is found in the WebAuthn relying party operations of Spring Security.
UserVerificationRequirement is a serializable constant holder rather than an enum, and it declares no equals, no hashCode, and no readResolve:
public final class UserVerificationRequirement implements Serializable {
@Serial
private static final long serialVersionUID = -2801001231345540040L;
public static final UserVerificationRequirement DISCOURAGED = new UserVerificationRequirement("discouraged");
public static final UserVerificationRequirement PREFERRED = new UserVerificationRequirement("preferred");
public static final UserVerificationRequirement REQUIRED = new UserVerificationRequirement("required");
private final String value;
UserVerificationRequirement(String value) {
this.value = value;
}
public String getValue() {
return this.value;
}
}
Webauthn4JRelyingPartyOperations then decides whether user verification must be enforced by comparing object identity against the REQUIRED constant, once while finishing registration and once while finishing authentication:
boolean userVerificationRequired = creationOptions.getAuthenticatorSelection()
.getUserVerification() == UserVerificationRequirement.REQUIRED;
boolean userVerificationRequired = request.getRequestOptions()
.getUserVerification() == UserVerificationRequirement.REQUIRED;
The registration and authentication options that carry this setting are held in the HTTP session between the start and the finish of a WebAuthn ceremony. When the session is stored in process, the value read back is the same object as the static constant and the identity comparison holds. When the session is serialized and deserialized, as a distributed session store does on every request, deserialization creates a fresh UserVerificationRequirement instance whose value is still "required" but whose reference differs from the constant. Because the class defines no readResolve, nothing collapses that instance back onto the constant, so the comparison silently evaluates to false and userVerificationRequired is passed to the webauthn4j verification parameters as false.
An attacker who reaches the ceremony endpoints with a victim's authenticator therefore completes the assertion without the user verification flag being enforced. The same identity comparison pattern is used for PublicKeyCredentialType.PUBLIC_KEY when credential parameters are converted for webauthn4j, where a deserialized value causes correct parameters to be rejected as an unknown credential type. No special privileges and no user interaction are needed to attempt the flow, which is reflected in the CVSS vector AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N.
This vulnerability was introduced in 2024 with the introduction of WebAuthn in Spring Security 6.4.0.
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.
- Where an upgrade is not immediately possible, avoid the exposed configuration by not combining an explicit required user verification setting with a serializing distributed session store.
- Leverage a commercial support partner like HeroDevs for post-EOL security support.
Credits
- Yu Bao from PayPal Cybersecurity Team (finder)