pac4j CVSS 10 Auth Bypass Hits Spring Security: A Supply Chain Wake-Up Call for Java Startups
A CVSS 10 authentication bypass in pac4j-jwt lets attackers forge JWTs using only a public RSA key. Here's what this means for your SOC 2 compliance.
If your Java application uses Spring Security, Play Framework, Vert.x, or Javalin for authentication, there's a reasonable chance that a library you've never directly configured is handling your JWT validation. That library is pac4j. And as of this week, it has a CVSS 10 vulnerability that lets attackers forge authentication tokens using nothing more than your server's public RSA key.
CVE-2026-29000 is a maximum-severity authentication bypass in pac4j-jwt. No stolen credentials required. No brute force. No sophisticated exploit chain. An attacker grabs your public key - which is, by definition, public - and uses it to mint valid JWTs with whatever claims they want, including admin privileges. The vulnerability also allows raw JSON claims injection via JSON Web Encryption (JWE), giving attackers a second path to the same outcome.
I've seen a lot of critical CVEs come and go over the years. Most of them require some precondition that limits real-world impact: you need authenticated access, or you need a specific configuration, or the vulnerable component isn't internet-facing. This one has none of those safety nets. If your application uses pac4j-jwt for token validation and exposes a public RSA key (which is how RSA-based JWT verification is supposed to work), you are vulnerable. Pre-authentication. From the internet. With a public proof-of-concept already available.
What Happened: The Technical Breakdown
CodeAnt AI discovered CVE-2026-29000 and privately reported it to pac4j's maintainers, who released patches within two days. Here's what makes this vulnerability particularly dangerous:
| Attribute | Detail |
|---|---|
| CVE | CVE-2026-29000 |
| CVSS Score | 10.0 (Maximum) |
| Type | Authentication bypass via JWT forgery |
| Authentication Required | No |
| Secrets Required | No (uses public RSA key) |
| Proof of Concept | Publicly available |
| Active Exploitation | None observed yet |
| Discovery | CodeAnt AI |
The flaw is a logic error in how pac4j-jwt validates JSON Web Tokens. In a correctly implemented RSA-based JWT system, the server uses a private key to sign tokens and a public key to verify them. The private key never leaves the server. The public key is, well, public - it's shared with anyone who needs to verify tokens.
pac4j's implementation has a flaw in this validation logic that allows an attacker to craft tokens that pass verification using only knowledge of the public RSA key. Arctic Wolf Labs confirmed the attack path: "A threat actor only needs to access a server's public RSA key to attempt exploitation."
There's a second attack vector through JWE. Attackers can deploy raw JSON claims through JSON Web Encryption, bypassing the signature validation entirely and injecting arbitrary identity claims into the authentication flow. Both vectors achieve the same result: the attacker authenticates as any user, with any role, with any permissions.
Why Scanners Didn't Catch It
This is a logic flaw, not a buffer overflow or injection vulnerability. Standard SAST and DAST tools look for known vulnerability patterns - SQL injection, XSS, insecure deserialization. A logic error in cryptographic token validation doesn't match any of those patterns. Your security scanner could give pac4j a clean bill of health and this vulnerability would still be sitting in your authentication stack.
This is exactly the kind of issue that makes automated security tooling necessary but insufficient. You need the tools, but you also need a process for tracking advisories and applying patches when the tools can't catch the bug.
The Blast Radius: Hundreds of Downstream Packages
pac4j bills itself as a "security engine for Java." It's not a niche library. It provides authentication and authorization for some of the most widely-used Java frameworks:
- Spring Security (via spring-webmvc-pac4j and spring-security-pac4j)
- Play Framework (via play-pac4j)
- Vert.x (via vertx-pac4j)
- Javalin (via javalin-pac4j)
- Ratpack, Spark, Dropwizard, JBoss and others
The CodeAnt AI CEO noted that the attack surface includes "any internet-facing application or API gateway using the affected configuration." That's a broad statement, but it's accurate. If you're running a Java web application that authenticates users via JWT and uses any of these framework integrations, you need to check whether pac4j-jwt is in your dependency tree.
Here's the insidious part: you might not know you're using pac4j. If you pulled in a Spring Security starter or a Play Framework authentication plugin, pac4j could be three or four levels deep in your transitive dependency tree. Your pom.xml or build.gradle doesn't mention it. Your team has never configured it directly. But it's sitting there, validating every JWT that hits your application.
Checking Your Dependencies
For Maven projects:
mvn dependency:tree | grep pac4j
For Gradle projects:
gradle dependencies | grep pac4j
If either command returns results containing pac4j-jwt, you're affected. If you see pac4j dependencies but not pac4j-jwt specifically, you may still want to update as a precaution, but the critical vulnerability is in the JWT module.
Why This Is a SOC 2 Problem
Authentication isn't a nice-to-have security feature. It's a foundational control that SOC 2's Trust Services Criteria evaluate directly. When your authentication library has a CVSS 10 bypass, multiple TSC controls come into question simultaneously.
CC6.1: Logical Access Controls
SOC 2 CC6.1 requires that you implement logical access controls to restrict access to information assets. JWT-based authentication is how most modern web applications implement this control. If the library validating those tokens can be tricked into accepting forged tokens, your logical access controls are effectively non-functional.
Your auditor is going to ask: "How do you ensure that only authenticated and authorized users can access the system?" If your answer depends on pac4j-jwt and you haven't patched CVE-2026-29000, the honest answer is "we don't."
CC6.6: Security of System Boundaries
This criterion addresses the security of connections to systems outside the entity's boundaries. An internet-facing API that accepts JWTs is a system boundary. A CVSS 10 authentication bypass at that boundary is about as severe as boundary security failures get.
CC9.2: Third-Party Risk Management
pac4j is a third-party component in your authentication stack. SOC 2 expects you to assess and monitor the risks of third-party service providers and components. The questions an auditor will ask:
- Do you maintain an inventory of third-party libraries with access to authentication and authorization functions?
- Do you monitor security advisories for those libraries?
- What is your SLA for patching critical vulnerabilities in third-party components?
- Can you produce evidence that you patched CVE-2026-29000 within that SLA?
If you can't answer these questions with documented evidence, you have a finding.
The Transitive Dependency Problem
Here's what makes this particularly painful for compliance: most teams don't know pac4j is in their stack. Their vendor inventory lists "Spring Security" or "Play Framework." They monitor Spring Security advisories. But the vulnerability isn't in Spring Security - it's in a transitive dependency that Spring Security pulls in.
Your SaaS compliance stack needs to account for transitive dependencies, not just the top-level libraries your team chose deliberately. A Software Bill of Materials (SBOM) is the tool that makes this manageable, and if you're not generating one, this CVE is the reason to start.
What to Do Right Now
1. Check If You're Affected (5 Minutes)
Run the dependency tree commands above. If pac4j-jwt appears anywhere in your dependency tree, proceed to step 2. If it doesn't, you're clear on this specific CVE - but keep reading, because the process matters more than the patch.
2. Patch Immediately
Update to the patched version of pac4j-jwt. The maintainers released fixes within two days of disclosure. Check the pac4j GitHub repository for the specific version that includes the fix for CVE-2026-29000.
If you depend on pac4j transitively through a framework plugin (spring-webmvc-pac4j, play-pac4j, etc.), check whether the plugin has released an update that pulls in the patched pac4j version. If not, you can force the dependency version in your build tool:
Maven (dependency management override):
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.pac4j</groupId>
<artifactId>pac4j-jwt</artifactId>
<version>PATCHED_VERSION</version>
</dependency>
</dependencies>
</dependencyManagement>
Gradle (resolution strategy):
configurations.all {
resolutionStrategy {
force 'org.pac4j:pac4j-jwt:PATCHED_VERSION'
}
}
3. Check for Signs of Exploitation
Even though no active exploitation has been observed yet, a public PoC is available. If your application was internet-facing with pac4j-jwt handling authentication, review your logs for:
- Authentication events with unusual or unexpected user claims
- Admin-level access from IP addresses or user agents you don't recognize
- JWT tokens with claim structures that don't match your application's standard token format
- Spikes in authentication events from unknown sources
4. Generate an SBOM
If you don't already produce a Software Bill of Materials, start now. An SBOM gives you a complete inventory of every dependency in your application, including transitive dependencies like pac4j-jwt that you might not know about.
For Java projects, CycloneDX and SPDX are the two standard formats. CycloneDX has Maven and Gradle plugins that generate SBOMs as part of your build:
# Maven
mvn org.cyclonedx:cyclonedx-maven-plugin:makeBom
# Gradle (add plugin first)
gradle cyclonedxBom
Store the SBOM as a build artifact. When the next critical CVE drops, you can search your SBOM in seconds instead of running dependency tree commands across every repository.
5. Document Everything for Your Auditor
If you're pursuing or maintaining SOC 2 compliance, document:
- When you learned about CVE-2026-29000
- How you assessed whether you were affected (the dependency tree check)
- When you patched (timestamp, commit hash, deployment record)
- What you checked for signs of exploitation
- What process changes you're implementing to catch similar issues faster
This documentation is your audit trail. When your auditor asks about vulnerability management, you want to show a documented, repeatable process - not a Slack thread where someone said "hey did we patch that thing?"
Building the SCA Process SOC 2 Requires
Patching this one CVE is necessary but not sufficient. The structural problem is that most startups don't have a Software Composition Analysis (SCA) process that catches transitive dependency vulnerabilities before they become compliance incidents.
Here's what a minimal SCA process looks like:
Automated Dependency Scanning in CI/CD
Add an SCA tool to your CI/CD pipeline that scans every build for known vulnerabilities in direct and transitive dependencies. Free options that work well:
| Tool | Language Support | Cost |
|---|---|---|
| OWASP Dependency-Check | Java, .NET, Python, Ruby, Node.js | Free |
| Trivy | Broad (containers, filesystems, repos) | Free |
| Grype | Broad (SBOMs, containers, filesystems) | Free |
| npm audit / pip audit | JavaScript / Python | Free |
For Java specifically, OWASP Dependency-Check integrates directly with Maven and Gradle and would have flagged CVE-2026-29000 as soon as it was added to the NVD.
Advisory Monitoring
Subscribe to security advisories for your critical dependencies. For Java applications:
- GitHub Advisory Database (covers Maven Central)
- NVD feeds for CVE notifications
- Project-specific mailing lists or RSS feeds
GitHub's Dependabot can automate this. Enable it on your repositories and it will create pull requests when vulnerable dependencies are detected.
Patch Management SLAs
Define and document how quickly you'll patch vulnerabilities at each severity level:
| Severity | CVSS Range | Patch SLA |
|---|---|---|
| Critical | 9.0 - 10.0 | 24 hours |
| High | 7.0 - 8.9 | 7 days |
| Medium | 4.0 - 6.9 | 30 days |
| Low | 0.1 - 3.9 | Next release cycle |
CVE-2026-29000 is a 10.0. Under this SLA, you have 24 hours from the time you learn about it to deploy a patched version. If you can't meet that timeline, document the reason and the compensating controls you've put in place (for example, WAF rules, temporary network restrictions, or disabling the affected feature).
Why Auth Libraries Keep Breaking
This isn't the first time a widely-used authentication library has had a critical vulnerability, and it won't be the last. The pattern is structural:
Authentication libraries are high-value targets. A vulnerability in a logging library is annoying. A vulnerability in an authentication library bypasses every access control in the application. Attackers and researchers both focus disproportionate attention on auth code.
Cryptographic code is hard to get right. JWT validation involves RSA key management, signature verification, claims parsing, algorithm selection, and key ID resolution. Each step has subtle edge cases. pac4j's logic flaw was in the cryptographic validation path - the kind of bug that requires deep understanding of both the JWT specification and the library's implementation to find.
Transitive dependencies hide risk. When you choose Spring Security, you're implicitly choosing every library in Spring Security's dependency tree. Your team evaluated Spring Security's reputation, documentation, and community support. They probably didn't evaluate pac4j, because they didn't know they were choosing it.
Open-source maintainer capacity is finite. pac4j is maintained by a small team. They responded quickly to this disclosure - patches within two days is excellent. But the gap between "vulnerability exists" and "vulnerability is discovered" can be years. The CrackArmor vulnerabilities in Linux AppArmor sat undiscovered since 2017. We don't know how long CVE-2026-29000 has been present in pac4j's codebase.
The solution isn't to avoid open-source auth libraries. The alternative - writing your own JWT validation - is almost certainly worse. The solution is to know what you're running, monitor it actively, and have a process for responding when things break.
The Window Is Open
The PoC is public. Active exploitation hasn't been observed yet, but the gap between "PoC available" and "exploitation in the wild" is shrinking with every major vulnerability. CodeAnt AI's CEO highlighted the period between PoC release and patch adoption as the highest-risk window. That window is open right now.
If you're running Java applications with JWT authentication, check your dependency tree today. Not tomorrow. Not at the next sprint planning meeting. Today. The check takes five minutes. The patch takes an hour. The SOC 2 finding for an unpatched CVSS 10 in your authentication stack takes months to remediate.
Patch first. Build the process second. Document everything.
Keep reading:
- SOC 2 Compliance Explained: What It Is, Who Needs It, and How to Get Certified
- The Free Security Toolstack: Every Security Tool You Need for $0
- The SaaS Compliance Stack: SOC 2, ISO 27001, GDPR, and What Actually Matters
Need help auditing your Java dependency supply chain or building an SCA process for SOC 2? Let's talk.