Attack ChainAIDerived
Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain
-
AccessObtain JWT from misconfigured app
-
ExploitReplay token past intended expiry
-
ExecutionPlugin fails to enforce exp/maxAge
-
ImpactMaintain unauthorized authenticated access
Vulnerability Assessment
| Weakness |
CWE
OWASP Top 10:2025
A10:2025 - Mishandling of Exceptional Conditions
ASVS 5.0
V16 - Security Logging and Error Handling Error detection with action
|
| Exploitation Conditions | Exploitation requires the affected application to pass expiresIn, notBefore, or maxAge a time-span STRING in a format the plugin's parser cannot read - specifically a compound span (e.g. '1h30m'), a month unit (e.g. '1 month'), an ISO 8601 duration, a value using a decimal comma, or a value with surrounding whitespace. This is a non-default developer misconfiguration; applications that pass these options as a numeric count of seconds, or that use only simple parseable spans, are not affected. On the signing path the condition yields tokens with no exp claim (never expire); on the verification path it disables maxAge age-rejection. Limiting factors: the specific unparseable string must actually be in use, and an attacker generally needs to already possess a token (their own aged token or a stolen/leaked one) to derive benefit, which bounds real-world impact below the raw network-facing surface. |
| Impact | The input CVSS 3.1 vector (AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N, base 6.8, Medium) is internally consistent with the description: exploitation is network-facing but gated behind AC:H because it only manifests when a developer supplies a time-span string in one of the specific unparseable formats (compound span, month unit, ISO 8601 duration, decimal comma, or surrounding whitespace), and PR:L reflects that an attacker typically needs a token to benefit. No EPSS score was provided in the input, so exploitation probability cannot be quantified. This is NOT in CISA KEV and no public exploit code is referenced, so exploitation status is 'no public exploit identified at time of analysis.' Net assessment: this is a real but conditional weakness rather than a mass-exploitation priority - the dangerous outcome (non-expiring tokens / unenforced maxAge) only occurs in applications that happen to use the affected string formats, which is a developer misconfiguration rather than a default state. The C:H/I:H rating is reasonable because indefinitely valid tokens enable prolonged unauthorized authenticated access, but the Medium score and AC:H correctly signal this should be prioritized by teams running affected configurations, not treated as an urgent internet-wide emergency. EPSS is missing - treat likelihood as unquantified rather than low. |
| Exploit ScenarioDerived | A development team configures @fastify/jwt with a human-friendly lifetime such as expiresIn: '1 month' or a compound '1h30m', which the plugin's parser silently drops, so issued JWTs carry no exp claim and never expire. An attacker who obtains such a token - for example a leaked, logged, or previously stolen session JWT - can replay it indefinitely, and on the verification side a configured maxAge that should reject an aged token no longer does. No public proof-of-concept is referenced for this issue. |
Get an e-mail when this CVE is exploited, patched or re-scored. Free account.
Alert meAnalysisAIv1 – Oct 08, 2026
Tokens issued through @fastify/jwt before 10.2.3 can be created without an exp claim - and therefore never expire - when expiresIn, notBefore, or maxAge is given a time-span string the plugin's parser cannot read, such as a compound span like '1h30m', a month unit, an ISO 8601 duration, a value using a decimal comma, or a string with surrounding whitespace. The identical flaw silently disables maxAge age-rejection on the verification path, so a token that should be rejected as too old is accepted. The issue is tracked as CVSS 3.1 6.8 (Medium; AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N), and the vector's PR:L reflects that an attacker generally needs to already hold a token - their own aged one or a stolen/leaked one - to benefit, with AC:H because the unparseable string must actually be in use as a non-default developer misconfiguration; no public exploit code and no confirmed active exploitation (CISA KEV) were identified at time of analysis.
RemediationAI
Upgrade @fastify/jwt to version 10.2.3 or later (vendor-released patch, confirmed available and fixed in 10.2.3); the release is the only reliable fix because it changes the parser from silently discarding an unreadable span to refusing it. Where an immediate upgrade is not possible, restructure the affected configuration as a specific compensating control: pass expiresIn, notBefore, and maxAge as an integer number of seconds instead of a duration string - this bypasses the broken parser entirely but requires you to compute and maintain the numeric equivalents, and it removes the readability benefit of spans like '15m'. Additionally, validate that any time-span string supplied to these options resolves to a finite value before the plugin consumes it (for example a unit-test or startup-time assertion that the parsed value is a positive finite number), failing fast if it does not; the trade-off is that this check lives in your application rather than the library and must be applied to every code path that configures the plugin. Review existing application code and environment-driven configuration for compound spans, month units, ISO 8601 durations, decimal commas, or whitespace-padded values, and audit tokens already issued by affected configurations - since tokens minted without an exp claim never expire, consider rotating signing keys to invalidate them. Detailed guidance is in the GitHub advisory at https://github.com/fastify/fastify-jwt/security/advisories/GHSA-9x4w-r9p5-5h7m and the OpenJS CNA page at https://cna.openjsf.org/security-advisories.html.
Affected VersionsENISA
Affected Packages
Source: OSV (Open Source Vulnerabilities)
Affects your stack
Affects 1 package in euvd
Checking this CVE against your own stacks and servers is part of the Basic plan and up.
Technical ContextAI · show details
JSON Web Tokens carry temporal claims - exp (expiration) and nbf (not before) - and libraries typically also implement a local maxAge check during verification by comparing iat against the current time. @fastify/jwt accepts human-readable durations for these options and converts them internally to seconds before writing the claim or performing the age comparison. The root cause is CWE-390 (Detection of Error Condition Without Action): when its duration parser encounters a span it cannot interpret - a compound span, a month unit, an ISO 8601 duration, a decimal comma, or a whitespace-padded value - it detects the failure but takes no action, dropping the option silently instead of raising an error. On the signing path the dropped value means no exp claim is emitted at all, producing a token that never expires; on the verification path the dropped maxAge value means the age comparison is skipped, so stale tokens pass. The CPE cpe:2.3:a:@fastify/jwt:@fastify/jwt:*:*:*:*:*:*:*:* with the EUVD range '@fastify/jwt 0 <10.2.3' scopes every release prior to the fix, i.e. the whole 0.x through 10.2.2 line; the plugin is a Fastify (Node.js) ecosystem component published under the OpenJS Foundation, which reported the issue.
Know when Fastify Jwt gets its next vulnerability
- Watch products and get an email when something new is published
- Check this CVE against your own stack and servers
- Exploit and PoC references, threat intelligence from 22 sources
No credit card · 7-day full trial
Severity by source
AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N
Manifests only with a specific non-default unparseable time-span configuration (AC:H) and an attacker holding a token (PR:L); non-expiring tokens enable sustained read/write access (C:H/I:H) with no availability impact.
Primary rating from Vendor (openjs).
CVSS VectorVendor: openjs
Lifecycle Timeline
1DescriptionCVE.org
@fastify/jwt is a JSON Web Token plugin for the Fastify web framework. In versions before 10.2.3, a time span passed to expiresIn, notBefore, or maxAge that the plugin's parser cannot read, such as a compound span, a month unit, an ISO 8601 duration, a decimal comma, or a value with surrounding whitespace, is silently dropped instead of refused. On the signing path this produces a token with no expiration claim that never expires, and on the verification path a configured maxAge stops being enforced, so a token that should be rejected for age is accepted. The issue is fixed in @fastify/jwt 10.2.3, and users should upgrade to 10.2.3 or later. As a workaround, pass these options as a number of seconds, or verify that any time-span string parses to a finite value before relying on it.
Threat Intelligence Overview
How this decision was reached
The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.
What every action level means
SSVC AssessmentENISA
Priority Score
Attack Technique
Information disclosure occurs when an application unintentionally exposes sensitive data that aids attackers in reconnaissance or directly compromises security.
EUVD-2026-94851
NVD
CVE.org
EPSS
Vulnrichment
GitHub
Exploit-DB