CVE-2026-29000: How a Public Key Breaks Authentication in pac4j-jwt
A critical authentication bypass in the Java security library pac4j-jwt allows attackers to impersonate any user, including administrators, using nothing more than a server's RSA public key. The vulnerability, tracked as CVE-2026-29000, carries a CVSS 10.0 score and affects all versions of org.pac4j:pac4j-jwt prior to 4.5.9, 5.7.9, and 6.3.3.
The flaw is straightforward to exploit. A public proof-of-concept exists, and patched versions are available now. If your Java application uses pac4j for JWT-based authentication, keep reading.
TL;DR
Detail | Value |
|---|---|
CVE | |
CVSS 3.1 | 10.0 Critical ( |
CVSS 4.0 | 10.0 Critical ( |
CWE | CWE-347: Improper Verification of Cryptographic Signature |
Maven artifact |
|
Affected | 4.x \< 4.5.9, 5.0.0-RC1 \< 5.7.9, 6.0.4.1 \< 6.3.3 |
Fixed | 4.5.9, 5.7.9, 6.3.3 |
Exploit Status | Public PoC available |
Discovered by | CodeAnt AI Security Research |
What is pac4j?
pac4j is a security framework for Java that provides authentication and authorization across multiple protocols: OAuth, SAML, CAS, OpenID Connect, and JWT. The pac4j-jwt module specifically handles JSON Web Token creation and validation through its JwtAuthenticator class.
pac4j can be used as the underlying authentication engine in frameworks like Spring Security, JEE, Play, Vert.x, and others. It is also widely deployed in CAS (Central Authentication Service) environments, which are common in universities and government institutions. If your application uses any pac4j-based authentication with JWTs, it may be affected.
How the vulnerability works
To understand this vulnerability, you need to know the difference between two types of JWTs:
JWS (JSON Web Signature): A signed token. The signature proves the token hasn't been tampered with and was created by someone with the signing key.
JWE (JSON Web Encryption): An encrypted token. The encryption protects the token's contents from being read in transit. JWE can wrap a JWS inside it for both confidentiality and integrity.
When pac4j's JwtAuthenticator receives an encrypted JWT (JWE), it follows this process:
Decrypt the outer JWE envelope
Parse the inner payload
Verify the signature on the inner token
The vulnerability is in step 3. After decrypting the JWE, the library calls com.nimbusds:nimbus-jose-jwt function toSignedJWT() on the inner payload to extract the signed token for verification. If the inner token is a PlainJWT (a JWT with alg: none, meaning no signature at all), this method returns null.
In the vulnerable code, this null value caused the entire signature verification block to be skipped. The library effectively said: "I can't find a signature to verify, so I'll just move on."
Here's a simplified view of the vulnerable logic:
The fix adds an explicit rejection when a non-signed token is found inside an encrypted envelope:
The attack in practice
An attacker needs only one thing: the server's RSA public key. By definition, public keys are... public. They're often embedded in JWKS endpoints, certificates, or configuration files.
The attack steps:
Obtain the server's RSA public key (from a JWKS endpoint, TLS certificate, or any public source)
Create a PlainJWT with arbitrary claims:
Wrap it in a JWE encrypted with the server's RSA public key
Send the forged token to the application
The application decrypts the JWE (the public key encryption is valid), finds no signature to verify, skips verification, and accepts the forged claims as authentic
The attacker never needs the private key, a shared secret, or any credentials. The public key that was supposed to protect the system is the only ingredient required.
Who is affected
You are potentially affected if:
You are using RSA encryption. This is detectable by RSAEncryptionConfiguration in the project.
Your application uses
org.pac4j:pac4j-jwtfor JWT authenticationYou use both encryption (JWE) and signature verification (JWS) configurations in your
JwtAuthenticatorYou are running any version prior to 4.5.9, 5.7.9, or 6.3.3
Applications that only use signed JWTs (JWS) without encryption are not vulnerable to this specific attack, as the PlainJWT-inside-JWE trick requires encryption to be configured.
Check with Snyk
You can check whether your project is affected using the Snyk CLI:
Snyk tracks this vulnerability as SNYK-JAVA-ORGPAC4J-15428218. If the vulnerable package is in your dependency tree, Snyk will flag it and recommend the upgrade path.
If you use Maven, the Snyk Maven plugin can also catch this as part of your build cycle without requiring a separate CLI step. For Gradle projects, the Snyk Gradle plugin provides the same integrated scanning capability.
You can also search your dependencies directly:
Remediation
Upgrade org.pac4j:pac4j-jwt to the patched version for your major version line:
Current Version | Upgrade To |
|---|---|
4.x | 4.5.9 or later |
5.x | 5.7.9 or later |
6.x | 6.3.3 or later |
For Maven, update your pom.xml:
For Gradle:
If you can't upgrade immediately
While upgrading is strongly recommended, you can reduce your exposure by:
Reviewing your JWT configuration: If your application only uses signed JWTs (JWS) without encryption, you're not vulnerable to this specific attack path. However, upgrading is still the safest course of action.
Auditing JWT-handling code: Ensure your application explicitly requires signed tokens and rejects unsigned ones at the application layer.
There is no configuration-level workaround within pac4j itself for the vulnerable versions.
Why this matters beyond pac4j
This vulnerability is a textbook example of a broader class of JWT implementation flaws. The core issue, treating the absence of a security property (a signature) as something to skip rather than something to reject, has appeared in JWT libraries across languages for years.
The lesson is consistent: cryptographic verification must be required, not optional. When a library encounters an unsigned token where a signed one is expected, the only safe response is rejection. Silent fallthrough is a vulnerability waiting to happen. For a deeper dive into how authentication flaws like this manifest, explore the Broken Authentication lesson on Snyk Learn.
For Java teams, this is a reminder to:
Explicitly configure accepted algorithms in your JWT validation. Never allow
none.Treat encryption and signing as independent concerns. Decryption proves confidentiality, not authenticity. A JWE-wrapped token still needs its signature verified.
Regularly audit transitive dependencies. pac4j-jwt might be pulled in by a framework integration you don't directly manage. See Best Practices for Managing Java Dependencies for guidance on auditing your dependency tree.
For more on secure JWT handling practices, see Top 3 Security Best Practices for Handling JWTs and Can Snyk Detect JWT Security Issues? on the Snyk blog.
Timeline
Date | Event |
|---|---|
March 2, 2026 | the pac4j-jwt project maintainer released patches |
March 3, 2026 | the pac4j-jwt project maintainer published an advisory and blog post |
March 4, 2026 | CVE details published on MITRE |
WHITEPAPER
The AI Security Crisis in Your Python Environment
As development velocity skyrockets, do you actually know what your AI environment can access?