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

  1. Upgrade fast-jwt to 6.3.4 or later and regenerate the lockfile so @fastify/jwt's ^6.2.4 range actually resolves there.
  2. Pass an explicit, asymmetric-only algorithms list to every verifier that uses a public key. The vendor states this prevents the attack on every version.
  3. Make sure the signing secret or key cannot be empty: fail startup if the environment variable is unset.
  4. 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