What a CVSS 10.0 bug revealed
CVE-2026-29000, disclosed in 2026, is an authentication bypass vulnerability in the JWT verification component of a Java library. It carries a CVSS score of 10.0 — as severe as the scale gets — and is classified under CWE-347, Improper Verification of Cryptographic Signature. An attacker who simply had access to the server's RSA public key could forge a token with arbitrary subject and role claims, authenticating as any user, including administrators.
The specific bug is library-specific, but the root cause behind it is a pattern that keeps resurfacing across JWT implementations in general. Rather than dissecting one product's flaw, this post looks at why JWT verification breaks this often, and what to actually check in your own implementation.
Why JWT verification is structurally fragile
JWT comes in two distinct forms: signed tokens (JWS) and encrypted tokens (JWE). Most vulnerabilities in this class trace back to verification logic that doesn't correctly enforce the boundary between the two.
The recurring patterns look like this:
- Algorithm confusion attacks. Verification code trusts the
algfield in the token header unconditionally, and ends up accepting an algorithm it never intended to support (likenone). - JWE/JWS conflation. A token that's supposed to be encrypted gets handed to signature-verification logic without the decryption step actually being enforced.
- Unverified
typ/ctyclaims. Code processes a token without checking what kind of token it actually is, accepting a token meant for one purpose as if it were an authentication token.
CVE-2026-29000 falls squarely into the second pattern: wrapping a plain, unsigned JWT inside an encrypted JWE effectively bypassed signature verification altogether.
Principles to enforce in your own implementation
When webhani reviews client JWT implementations, these are the checks we always run.
1. Explicitly allowlist algorithms
// Bad: trusting whatever algorithm the token header specifies
function verifyBad(token: string, key: CryptoKey) {
return jwt.verify(token, key); // no algorithm constraint
}
// Good: the verifier states what it expects, not the token
function verifyGood(token: string, key: CryptoKey) {
return jwt.verify(token, key, {
algorithms: ["RS256"], // only this algorithm is accepted
});
}The principle: the verifier decides which algorithm is acceptable — it never takes the token's word for it.
2. Treat token type as a distinct branch, not an afterthought
Don't run JWE and JWS through the same function. Branch explicitly at parse time.
function parseToken(token: string) {
const parts = token.split(".");
if (parts.length === 5) {
// 5-part structure — treat as an encrypted JWE
return decryptJWE(token);
}
if (parts.length === 3) {
// 3-part structure — treat as a signed JWS
return verifyJWS(token);
}
throw new Error("Unrecognized token format");
}Deciding the format at parse time, rather than downstream, closes off the exact failure mode where an encrypted token accidentally lands in signature-verification code.
3. Use a maintained library, current version — don't roll your own
Writing JWT verification logic from scratch is a mistake to avoid entirely. Use a widely adopted, actively maintained library (jose is a solid example) and keep it current. When a CVE like this one lands, the difference between being exposed for hours versus months usually comes down to whether your dependency-patching process is fast.
An operational checklist
- Does your JWT library have an explicit algorithm allowlist configured?
- Are JWE and JWS processed through separate, explicit code paths?
- Is there a process for tracking CVEs in your JWT/auth dependencies?
- Is the
nonealgorithm, or an unspecified algorithm, explicitly rejected?
Takeaway
CVE-2026-29000 is a specific product's bug, but its root cause — a blurry boundary between encryption and signing, and unconditional trust in algorithm headers — is a risk shared by any system that handles JWTs. Because a broken auth layer has an outsized blast radius, explicit algorithm allowlisting and strict token-type separation should be non-negotiable implementation principles, not optional hardening. webhani helps clients strengthen exactly this kind of authentication infrastructure through security review.
Source: pac4j-jwt Security Risks, CVE-2026-29000 (Penligent)