GitHub Security Advisory: GHSA-h395-gr6q-cpjc
Package: jsonwebtoken (RUST)
Severity: MODERATE (CVSS 0.0)
CWE: CWE-843: Access of Resource Using Incompatible Type ('Type Confusion')
Vulnerable versions: < 10.3.0
Patched in: 10.3.0

Summary: jsonwebtoken has Type Confusion that leads to potential authorization bypass

Details:
## Summary:

It has been discovered that there is a Type Confusion vulnerability in jsonwebtoken, specifically, in its claim validation logic.

When a standard claim (such as nbf or exp) is provided with an incorrect JSON type (Like a String instead of a Number), the library’s internal parsing mechanism marks the claim as “FailedToParse”. Crucially, the validation logic treats this “FailedToParse” state identically to “NotPresent”.

This means that if a check is enabled (like: validate_nbf = true), but the claim is not explicitly marked as required in required_spec_claims, the library will skip the validation check entirely for the malformed claim, treating it as if it were not there. This allows attackers to bypass critical time-based security restrictions (like “Not Before” checks) and commit potential authentication and authorization bypasses.

## Details:

The vulnerability stems from the interaction between the TryParse enum and the validate function in [src/validation.rs](https://github.com/Keats/jsonwebtoken/blob/master/src/validation.rs).

 1. The TryParse Enum: The library uses a custom TryParse enum to handle claim deserialization:
```
enum TryParse<T> {
    Parsed(T),
    FailedToParse, // Set when deserialization fails (e.g. type mismatch)
    NotPresent,
}
```
If a user sends {“nbf”: “[PII_REDACTED]9”} (legacy/string format), serde fails to parse it as u64, and it results in TryParse::FailedToParse.

 1. The Validation Logic Flaw (src/validation.rs): In Validation::validate, the code checks for exp and nbf
like this:
```
// L288-291
if matches!(claims.nbf, TryParse::Parsed(nbf) if options.validate_nbf && nbf > now + options.leeway) {
    return Err(new_error(ErrorKind::ImmatureSignature));
}
```
This matches! macro explicitly looks for TryParse::Parsed(nbf).

 • If claims.nbf is FailedToParse, the match returns false.
 • The if block is skipped.
 • No error is returned.
 1. The “Required Claims” Gap: The only fallback mechanism is the “Required Claims” check:
```
// Lines 259-267
for required_claim in &options.required_spec_claims {
    let present = match required_claim.as_str() {
        "nbf" => matches!(claims.nbf, TryParse::Parsed(_)),
        // ...
    };
    if !present { return Err(...); }
}
```
If “nbf” IS in required_spec_claims, FailedToParse will fail the matches!(..., Parsed(_)) check, causing the present to be false, and correctly returning an error.

However, widely accepted usage patterns often enable validation flags (validate_nbf = true) without adding the claim to the required list, assuming that enabling validation implicitly requires the claim’s validity if it appears in the token. jsonwebtoken seems to violate this assumption.

Environment:

 • Version: jsonwebtoken 10.2.0
 • Rust Version: rustc 1.90.0
 • Cargo Version: cargo 1.90.0
 • OS: MacOS Tahoe 26.2

POC:

For demonstrating, Here is this simple rust code that demonstrates the bypass. It attempts to validate a token with a string nbf claiming to be valid only