fast-jwt (under @fastify/jwt, 1.3M weekly downloads): the second fix for RSA-to-HS256 algorithm confusion is bypassed again (CVE-2026-107722, 9.8, 6.2.0 to 6.2.4, fixed 6.3.0); raw JWK text treated as an HMAC secret, unsigned tokens accepted on an empty key, expired tokens served from cache; vendor July 28, CVEs 2026-10-08
TL;DR
fast-jwt, the JWT library that @fastify/jwt depends on (1,291,992 and 971,995 weekly npm downloads respectively, 2026-10-01 to 10-07), has now had the same RSA-to-HS256 algorithm-confusion bug fixed three times. CVE-2023-48223 was the original. CVE-2026-34950 (2026-04-02, 9.1) found that the fix's PEM-header check was anchored to the start of the string, so a leading space defeated it. CVE-2026-107722 (vendor advisory 2026-07-28, CVE 2026-10-08, CVSS 9.8) finds that the second fix only trims whitespace, so any non-whitespace prefix, a comment, a control character, a zero-width character or wrapper text, still makes the library treat an RSA public key as an HMAC secret. An attacker who holds the public key signs HS256 tokens with it and the verifier accepts them. Affected 6.2.0 to 6.2.4, fixed 6.3.0. The same July batch adds raw JWK/JWKS text accepted as an HMAC secret (CVE-2026-107724, 7.4, 6.2.4 only), unsigned tokens accepted when the key is '' or null and algorithms is set (CVE-2026-107720, 7.4, fixed 6.3.1), and a verifier cache that serves expired tokens for the cache TTL when iat is absent (CVE-2026-107719, 4.2, 2026-10-04, fixed 6.3.4). Upgrade to 6.3.4, and set an asymmetric-only algorithms list, which the vendor says prevents the confusion attack regardless of version.
What happened
The vendor published the July advisories on its own tab; GitHub assigned the CVEs on 2026-10-08, which is when the 9.8 reached the reviewed-critical npm listing. Dated by the vendor.
| Vendor date | Advisory | CVE | Score | Bug | Affected | Fixed |
|---|---|---|---|---|---|---|
| 2026-07-28 | GHSA-ww5h-9m49-7xx4 | CVE-2026-107722 | 9.8 | performDetectPublicKeyAlgorithms trims whitespace but publicKeyPemMatcher stays start-anchored, so a non-whitespace prefix skips PEM detection and the key falls through to the HMAC path |
6.2.0 to 6.2.4 | 6.3.0 (PR #632) |
| 2026-07-28 | GHSA-g3jj-5cmm-3hxx | CVE-2026-107724 | 7.4 | A serialised public JWK or JWKS passed as the verifier key is not recognised as asymmetric and is classified as a plain HMAC secret; an attacker who reproduces the exact string signs HS256 claims with it | 6.2.4 | 6.3.0 |
| 2026-07-30 | GHSA-8wpc-h4q6-8fxv | CVE-2026-107720 | 7.4 | createVerifier with key: '' or null and a non-empty algorithms list skips signature verification; an app whose secret comes from an unset environment variable accepts unsigned tokens |
≤ 6.3.0 | 6.3.1 |
| 2026-07-28 | GHSA-5hjw-83fp-phq9 | none seen | High | Silent claim-validator bypass when the JWT payload is a JSON array (title and severity from the tab; not opened) | ||
| 2026-07-28 | GHSA-687g-22h4-j4w4 | none seen | Moderate | clockTolerance: Infinity bypasses exp and nbf and persists in the verifier cache (title from the tab; not opened) |
||
| 2026-10-04 | GHSA-x937-hj6v-793p | CVE-2026-107719 | 4.2 | The cache deadline is taken from exp only when iat is present, so a token with exp and no iat stays valid in the cache for the default 10-minute TTL after it expired |
≤ 6.3.3 | 6.3.4 |
The preconditions matter for severity. The 9.8 needs a verifier that holds a public key and lets HS256 be inferred or allowed, which is the default shape when no algorithms option is set. An app that passes algorithms: ['RS256'] is not exploitable on any version. The JWK variant needs raw JSON key text as the key. The empty-key variant needs a missing secret plus an explicit algorithms list, which is exactly what a misconfigured deployment looks like.
Who consumes it. @fastify/jwt 10.2.4 declares fast-jwt: ^6.2.4, so a fresh install today resolves to 6.3.4, but a lockfile that pinned 6.2.4 in July stays on the vulnerable version until it is regenerated. This is the JavaScript twin of the Python findings the corpus already carries: PyJWT's PEM-detection bypass (CVE-2026-102268, 2026-09-29) and python-jose's DER-key confusion (CVE-2026-85394, unpatched). Every library that guesses the key type from its text has now been re-broken at least once in 2026.
Credits. dc-infosec and EchoSkorJjj (DC INFOSEC / n0l3x) for CVE-2026-107722; the-vibe-dev for CVE-2026-107724; Char0n1507 for CVE-2026-107720; SomnathDas, euriconicacio, BlueN0r and kagebunsher for CVE-2026-107719.
Am I affected?
npm ls fast-jwt @fastify/jwt 2>/dev/null
grep -rn "createVerifier\|fastifyJwt\|@fastify/jwt" --include=*.js --include=*.ts . | grep -v node_modules | head
# vulnerable shape: a public key as `key` and no `algorithms` option, or `algorithms` set with an empty/unset key
Any resolved fast-jwt below 6.3.4 is missing at least one fix. The 9.8 applies to 6.2.0 through 6.2.4 when the verifier can infer HS256.
If you are affected
- Upgrade
fast-jwtto 6.3.4 or later and regenerate the lockfile so@fastify/jwt's^6.2.4range actually resolves there. - Pass an explicit, asymmetric-only
algorithmslist to every verifier that uses a public key. The vendor states this prevents the attack on every version. - Make sure the signing secret or key cannot be empty: fail startup if the environment variable is unset.
- If you cannot rule out forged tokens, rotate the signing key pair, which invalidates every token issued under it, and review authorisation logs for sessions that never went through login. See if-your-webapp-was-compromised.md.
Prevention
Never let the verifier infer the algorithm from the key material; the allowlist is the control (credential-hygiene.md). Keep auth primitives on latest and run npm audit in CI, because these fixes shipped as patch releases with no changelog warning (npm-hardening.md, package-vetting-checklist.md).
Sources
- GitHub Advisory Database — GHSA-ww5h-9m49-7xx4: Incomplete patch of CVE-2026-34950: Non-whitespace key-prefix re-enables RSA→HS256 algorithm confusion (fetched 2026-10-09; CVE-2026-107722, 9.8
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H, 6.2.0 to 6.2.4 → 6.3.0, repository advisory 2026-07-28, database 2026-10-08, PR #632, credits) - nearform/fast-jwt — Security Advisories (walked 2026-10-09; ten entries including the five of 2026-07-28/30 and the 2026-10-04 cache entry)
- GHSA-g3jj-5cmm-3hxx — fast-jwt 6.2.4 treats raw public JWK JSON as an HMAC secret (CVE-2026-107724, 7.4, = 6.2.4 → 6.3.0), GHSA-8wpc-h4q6-8fxv — createVerifier accepts unsigned JWTs when key is '' or null (CVE-2026-107720, 7.4, ≤ 6.3.0 → 6.3.1), GHSA-x937-hj6v-793p — Verifier cache accepts expired JWTs without iat (CVE-2026-107719, 4.2, ≤ 6.3.3 → 6.3.4)
- CVE Services — CVE-2026-107722, CVE-2026-107724, CVE-2026-107720, CVE-2026-107719 (GitHub as CNA, all published 2026-10-08) and CVE-2026-34950 (2026-04-06, 9.1, ≤ 6.1.0, the whitespace-prefix bypass of CVE-2023-48223)
- GitHub Advisory Database — reviewed critical npm listing (where the 2026-10-08 row surfaced); registry data via
npm view @fastify/jwt dependencies.fast-jwt(^6.2.4),npm view fast-jwt version(6.3.4) andapi.npmjs.orgweekly download points, all 2026-10-09