CVE-2026-65583
Patch Available.
This Vulnerability has been fixed in the Never-Ending Support (NES) version offered by HeroDevs.
Overview
Apache CXF is an open-source services framework, maintained by the Apache Software Foundation, for building and consuming web services in Java. It implements the JAX-WS and JAX-RS APIs and supports SOAP, the WS-* standards, RESTful HTTP, and OAuth 2.0 over transports such as HTTP and JMS. Its modules are published as Maven artifacts under org.apache.cxf.
A Weak Authentication vulnerability (CVE-2026-65583) has been identified in the OpenID Connect relying-party ID token validation of Apache CXF, which allows attackers to have self-issued ID tokens accepted without the subject, audience, expiry, issued-at, and not-before checks, bypassing authentication controls when self-issued provider support is enabled.
Per OWASP: Confirmation of the user's identity, authentication, and session management is critical to protect against authentication-related attacks.
This issue affects versions prior to 3.6.12, 4.0.0 through 4.1.7, and 4.2.0 through 4.2.2 of Apache CXF.
Details
Module Info
- Product: Apache CXF
- Affected packages:
org.apache.cxf:cxf-rt-rs-security-sso-oidc - Affected versions: <3.6.12, >=4.0.0 <4.1.8, >=4.2.0 <4.2.3
- GitHub repository: https://github.com/apache/cxf
- Published packages: https://central.sonatype.com/artifact/org.apache.cxf/cxf-rt-rs-security-sso-oidc
- Package manager: Maven
- Fixed in:
- OSS Apache CXF 3.6.12, 4.1.8, 4.2.3
- NES for Apache CXF v3.5.13, v3.4.12
Vulnerability Info
This Critical-severity vulnerability is found in the org.apache.cxf:cxf-rt-rs-security-sso-oidc package in versions prior to 3.6.12, 4.0.0 through 4.1.7, and 4.2.0 through 4.2.2 of Apache CXF.
OidcClaimsValidator validates the claims of ID tokens received by a CXF OpenID Connect relying party. When supportSelfIssuedProvider is enabled and no issuerId is configured, any token whose iss claim is https://self-issued.me is routed to validateSelfIssuedProvider(). That method has an empty body, and the subject, authorized party, audience, expiry, issued-at, and not-before checks all live in the else branch, so none of them run for a self-issued token:
public void validateJwtClaims(JwtClaims claims, String clientId, boolean validateClaimsAlways) {
// validate the issuer
String issuer = claims.getIssuer();
if (issuer == null && validateClaimsAlways) {
throw new OAuthServiceException("Invalid issuer");
}
if (supportSelfIssuedProvider && issuerId == null
&& issuer != null && SELF_ISSUED_ISSUER.equals(issuer)) {
validateSelfIssuedProvider(claims, clientId, validateClaimsAlways);
} else {
if (issuer != null && !issuer.equals(issuerId)) {
throw new OAuthServiceException("Invalid issuer");
}
// validate subject
if (claims.getSubject() == null) {
throw new OAuthServiceException("Invalid subject");
}
// ... authorized party, audience, expiry, issuedAt and nbf checks ...
}
}
private void validateSelfIssuedProvider(JwtClaims claims, String clientId, boolean validateClaimsAlways) {
}The only binding a self-issued token is meant to carry, the requirement that the sub_jwk public key's JWK thumbprint equals sub, is attempted only while choosing a signature verifier. That code looks up the issuer under a non-standard claim named issuer instead of the registered iss claim, and it does not consult issuerId. As a result, any token that carries issuer set to https://self-issued.me is verified with the public key embedded in its own sub_jwk claim, whatever its iss says:
@Override
protected JwsSignatureVerifier getInitializedSignatureVerifier(JwtToken jwt) {
JsonWebKey key = null;
if (supportSelfIssuedProvider && SELF_ISSUED_ISSUER.equals(jwt.getClaim("issuer"))) {
String publicKeyJson = (String)jwt.getClaim("sub_jwk");
if (publicKeyJson != null) {
JsonWebKey publicKey = JwkUtils.readJwkKey(publicKeyJson);
String thumbprint = JwkUtils.getThumbprint(publicKey);
if (thumbprint.equals(jwt.getClaim("sub"))) {
key = publicKey;
}
}
// ...
}
// ...
}As a result, a relying party that enables self-issued provider support runs no claim checks on a token whose iss is https://self-issued.me. Expired tokens, tokens that are not yet valid, tokens issued for a different client, and tokens without a subject all pass claims validation, so replayed or misdirected self-issued tokens can be used to authenticate. Separately, because the verifier trusts the non-standard issuer claim, an attacker can sign a token with their own key, embed that key in sub_jwk, and set iss to the configured provider; the token is then verified with the attacker's key and accepted as one issued by that provider. Self-issued provider support is disabled by default, so only deployments that call setSupportSelfIssuedProvider(true) are exposed, whether or not an issuer id is configured.
Mitigation
Only recent versions of Apache CXF 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 Apache CXF release line (3.6.x or later) that contains the fix.
- Leverage a commercial support partner like HeroDevs for post-EOL security support.
Credits
- Guanping Zhang (finder)