{"id":"CVE-2026-35039","revision":null,"records":[{"revision":"bfc4b4b0ee9745868b5d18ad54f787b3","observedAt":"2026-10-23T15:48:27.903+00:00","statement":{"source":"cve-program","id":"CVE-2026-35039","aliases":[],"title":"fast-jwt Affected by Cache Confusion via cacheKeyBuilder Collisions Can Return Claims From a Different Token (Identity/Authorization Mixup)","description":"fast-jwt provides fast JSON Web Token (JWT) implementation. From 0.0.1 to before 6.2.0, setting up a custom cacheKeyBuilder method which does not properly create unique keys for different tokens can lead to cache collisions. This could cause tokens to be mis-identified during the verification process leading to valid tokens returning claims from different valid tokens and users being mis-identified as other users based on the wrong token. Version 6.2.0 contains a patch.","state":0,"publishedAt":"2026-04-06T16:59:43.124+00:00","modifiedAt":"2026-05-13T15:12:43.989+00:00","affected":[{"ecosystem":"Vendor product","name":"fast-jwt","ranges":[],"versions":[],"rawVersionEvidence":"{\n                    \"vendor\": \"nearform\",\n                    \"product\": \"fast-jwt\",\n                    \"versions\": [\n                        {\n                            \"version\": \">= 0.0.1, < 6.2.0\",\n                            \"status\": \"affected\"\n                        }\n                    ]\n                }"}],"metrics":[{"system":"CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N","score":9.1,"severity":"CRITICAL"}],"references":["https://github.com/nearform/fast-jwt/security/advisories/GHSA-rp9m-7r4c-75qg","https://github.com/nearform/fast-jwt/commit/de121056c6415b58770c60640881eaec67ac4ceb"],"sourceUrl":"https://www.cve.org/CVERecord?id=CVE-2026-35039","license":"CVE Program Terms of Use","key":"cve-program:CVE-2026-35039","identifiers":["CVE-2026-35039"]},"retained":false,"historicalAffected":[]},{"revision":"c5df71b2f6cd4d768ccfbd4f26baa26e","observedAt":"2026-10-25T08:17:35.884+00:00","statement":{"source":"nvd","id":"CVE-2026-35039","aliases":[],"title":"fast-jwt provides fast JSON Web Token (JWT) implementation. From 0.0.1 to before 6.2.0, setting up a custom cacheKeyBuilder method which does not properly create unique keys for different tokens can lead to cache collisions. This could caus…","description":"fast-jwt provides fast JSON Web Token (JWT) implementation. From 0.0.1 to before 6.2.0, setting up a custom cacheKeyBuilder method which does not properly create unique keys for different tokens can lead to cache collisions. This could cause tokens to be mis-identified during the verification process leading to valid tokens returning claims from different valid tokens and users being mis-identified as other users based on the wrong token. Version 6.2.0 contains a patch.","state":0,"publishedAt":"2026-04-06T17:17:13.243+00:00","modifiedAt":"2026-10-23T23:10:00.157+00:00","affected":[],"metrics":[{"system":"CVSS 3.1 · security-advisories@github.com · Secondary","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N","score":9.1,"severity":"CRITICAL"}],"references":["https://github.com/nearform/fast-jwt/commit/de121056c6415b58770c60640881eaec67ac4ceb","https://github.com/nearform/fast-jwt/security/advisories/GHSA-rp9m-7r4c-75qg"],"sourceUrl":"https://nvd.nist.gov/vuln/detail/CVE-2026-35039","license":"NIST NVD usage terms; underlying source attribution retained","enrichment":{"sourceStatus":"Analyzed","weaknesses":["CWE-345","CWE-706","CWE-1289"],"productCriteria":["cpe:2.3:a:nearform:fast-jwt:*:*:*:*:*:node.js:*:*"],"sourceDates":{"published":"2026-04-06T17:17:13.243","lastModified":"2026-10-23T23:10:00.157"},"relatedAdvisories":[],"note":"NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON."},"key":"nvd:CVE-2026-35039","identifiers":["CVE-2026-35039"]},"retained":false,"historicalAffected":[]},{"revision":"6f3baf396414414d9a8adbbc14e63d26","observedAt":"2026-10-24T13:17:17.793+00:00","statement":{"source":"osv","id":"GHSA-rp9m-7r4c-75qg","aliases":["CVE-2026-35039"],"title":"fast-jwt: Cache Confusion via cacheKeyBuilder Collisions Can Return Claims From a Different Token (Identity/Authorization Mixup)","description":"_NOTE_: While the library exposes a mechanism which could introduce the vulnerability, this issue is created by developer-supplied code and not by the library itself. We will add a warning and some education for users around the possible issues however since the defaults work we will not be updating the library beyond that for this advisory.\n\n## Impact\n\nSetting up a custom cacheKeyBuilder method which does not properly create unique keys for different tokens can lead to cache collisions. This could cause tokens to be mis-identified during the verification process leading to:\n\n- Valid tokens returning claims from different valid tokens\n- Users being mis-identified as other users based on the wrong token\n\nThis could result in:\n- User impersonation - UserB receives UserA's identity and permissions\n- Privilege escalation - Low-privilege users inherit admin-level access\n- Cross-tenant data access - Users gain access to other tenants' resources\n- Authorization bypass - Security decisions made on wrong user identity\n\n## Affected Configurations\n\nThis vulnerability ONLY affects applications that BOTH:\n\n1. Enable caching using the cache option\n2. Use custom cacheKeyBuilder functions that can produce collisions\n\nVULNERABLE examples:\n```\n// Collision-prone: same audience = same cache key\ncacheKeyBuilder: (token) => {\n  const { aud } = parseToken(token)\n  return `aud=${aud}`\n}\n\n// Collision-prone: grouping by user type\ncacheKeyBuilder: (token) => {\n  const { aud } = parseToken(token)\n  return aud.includes('admin') ? 'admin-users' : 'regular-users'\n}\n\n// Collision-prone: tenant + service grouping\ncacheKeyBuilder: (token) => {\n  const { iss, aud } = parseToken(token)\n  return `${iss}-${aud}`\n}\n```\n\nSAFE examples:\n```\n// Default hash-based (recommended)\ncreateVerifier({ cache: true })  // Uses secure default\n\n// Include unique user identifier\ncacheKeyBuilder: (token) => {\n  const { sub, aud, iat } = parseToken(token)\n  return `${sub}-${aud}-${iat}`\n}\n\n// No caching (always safe)\ncreateVerifier({ cache: false })\n```\n### Not Affected\n\n- Applications using **default caching**\n- Applications with **caching disabled**\n \n## Assessment Guide\n\nTo determine if you're affected:\n\n1. Check if caching is enabled: Look for cache: true or cache: <number> in verifier configuration\n2. Check for custom cache key builders: Look for cacheKeyBuilder function in configuration\n3. Analyze collision potential: Review if your cacheKeyBuilder can produce identical keys for different users/tokens\n4. If no custom cacheKeyBuilder: You are NOT affected (default is safe)\n\n## Mitigations\n\nMitigations include:\n\n- Ensure uniqueness of keys produced in cacheKeyBuilder\n- Remove custom cacheKeyBuilder method\n- Disable caching\n\n---\n\nfast-jwt allows enabling a verification cache through the cache option.\nThe cache key is derived from the token via cacheKeyBuilder.\n\nWhen a custom cacheKeyBuilder produces collisions between different tokens, the verifier may return the cached payload of a previous token instead of validating and returning the payload of the current token.\n\nThis results in cross-token payload reuse and identity confusion.\n\nTwo distinct valid JWTs can be verified successfully but mapped to the same cached entry, causing the verifier to return claims belonging to a different token.\n\nThis affects authentication and authorization decisions when applications trust the returned payload.\n\nAffected component\n\nsrc/verifier.js\n\nRelevant logic:\n\ncache enabled via createCache\n\ncache population via cacheSet\n\nlookup based on cacheKeyBuilder(token)\n\ncached payload returned without re-verification\n\nImpact\n\nIdentity / authorization confusion via cache collision.\n\nIf two tokens generate the same cache key:\n\ntoken A is verified → payload stored in cache\n\ntoken B is verified → cache hit occurs\n\nverifier returns payload from token A instead of B\n\nObserved effect:\n\nsubject mismatch\n\nclaim mismatch\n\nauthorization decision performed on wrong identity\n\nPotential real-world consequences:\n\nuser impersonation (logical)\n\nprivilege confusion\n\nincorrect RBAC evaluation\n\ngateway / middleware auth inconsistencies\n\nThis is especially dangerous when:\n\ncache is enabled (recommended for performance)\n\ncustom cacheKeyBuilder is used\n\nidentity claims (sub / aud / iss) drive authorization\n\nRoot cause\n\nThe verifier assumes the cache key uniquely identifies the token and its claims.\n\nHowever:\n\ncacheKeyBuilder is user-controlled\n\ncollisions are not detected\n\ncache entries store decoded payload\n\ncached payload is returned without binding validation\n\nThis creates a trust boundary break between:\n\ntoken → cache key → cached payload\n\nProof of concept\n\nEnvironment:\n\nfast-jwt: 6.1.0\n\nNode.js: v24.13.1\n\nPoC:\n\nconst { createSigner, createVerifier } = require('fast-jwt')\n\nconst sign = createSigner({ key: 'secret' })\n\n// Two distinct tokens\nconst t1 = sign({ sub: 'userA', aud: 'admin' })\nconst t2 = sign({ sub: 'userB', aud: 'admin' })\n\n// Deliberately unsafe cache key builder (collision)\nconst verify = createVerifier({\n  key: 'secret',\n  cache: true,\n  cacheKeyBuilder: () => 'static-key'\n})\n\nconsole.log('verify t1')\nconst p1 = verify(t1)\nconsole.log('t1 PASS sub=', p1.sub)\n\nconsole.log('verify t2')\nconst p2 = verify(t2)\nconsole.log('t2 PASS sub=', p2.sub)\n\nconsole.log('verify t2 again')\nconst p3 = verify(t2)\nconsole.log('t2-again PASS sub=', p3.sub)\n\nconsole.log('verify t1 again')\nconst p4 = verify(t1)\nconsole.log('t1-again PASS sub=', p4.sub)\n\nObserved output:\n\nverify t1\nt1 PASS sub= userA\n\nverify t2\nt2 PASS sub= userA\n\nverify t2 again\nt2-again PASS sub= userA\n\nverify t1 again\nt1-again PASS sub= userA\n\nThe verifier returns payload from userA when verifying userB.\n\nExpected behavior\n\nCache must not allow returning claims from a different token.\n\nVerification must remain bound to the actual token being validated.\n\nEven if cache collisions occur, the verifier should:\n\nrevalidate signature\n\nre-decode payload\n\nor invalidate cache entry\n\nWhy this is not “just misuse”\n\nThis is not merely a user mistake.\n\nReasons:\n\nfast-jwt explicitly exposes cacheKeyBuilder as an extension point.\n\nThe documentation suggests performance tuning via custom key builders.\n\nNo safeguards exist against collisions.\n\nNo verification binding is performed between:\n\ncached payload\n\noriginal token\n\nThe verifier trusts cache output as authoritative identity.\n\nThis creates a security-sensitive invariant:\n\n\"cache key uniqueness\"\n\nwhich is neither enforced nor validated.\n\nSecurity-critical libraries must assume extension hooks can be misused and implement defensive checks, especially when identity decisions are derived from cached values.\n\nSecurity classification\n\nlogical authorization flaw\n\ncache confusion vulnerability\n\nidentity boundary break\n\nClosest CWE:\n\nCWE-440 — Expected Behavior Violation\n\nSuggested fix (minimal and safe)\n\nBind cache entries to token integrity.\n\nOption A — safest:\n\nStore token hash along with payload and verify match before returning cache.\n\nConceptual patch:\n\nconst tokenHash = hashToken(token)\n\ncache.set(key, { tokenHash, payload })\n\n...\n\nconst entry = cache.get(key)\n\nif (entry && entry.tokenHash === hashToken(token)) {\n  return entry.payload\n}\n\nOption B — simpler:\n\nDisable cache usage when custom cacheKeyBuilder is provided.\n\nOption C — defensive:\n\nAlways re-validate signature when cache hit occurs.\n\nNotes\n\nDefault cacheKeyBuilder is safe (hash-based).\n\nIssue appears when custom builders are used — a documented and supported feature.\n\nImpact increases in:\n\nAPI gateways\n\nauth middleware\n\nRBAC layers relying on payload.sub / payload.aud\n\nThis vulnerability is independent from:\n\nRegExp statefulness issue\n\nReDoS claim validation issue\n\nIt is a separate flaw in cache design and trust model.\n\nPoC did on my computer:\n'use strict'\n\nconst fs = require('node:fs')\nconst path = require('node:path')\nconst { createSigner, createVerifier } = require('./src')\n\nfunction nowSec() {\n  return Math.floor(Date.now() / 1000)\n}\n\nconst sign = createSigner({ key: 'secret' })\nconst t1 = sign({ sub: 'userA', aud: 'admin', iat: nowSec() })\nconst t2 = sign({ sub: 'userB', aud: 'admin', iat: nowSec() })\n\nfunction badKeyBuilder() {\n  return 'aud=admin'\n}\n\nconst verify = createVerifier({\n  key: 'secret',\n  cache: true,\n  cacheTTL: 60000,\n  cacheKeyBuilder: badKeyBuilder\n})\n\nfunction run(tok) {\n  try {\n    const out = verify(tok)\n    return { ok: true, sub: out.sub, aud: out.aud }\n  } catch (e) {\n    return { ok: false, code: e.code || String(e), message: e.message }\n  }\n}\n\nconst results = []\nresults.push({ step: 'verify(t1)', token: 't1', result: run(t1) })\nresults.push({ step: 'verify(t2)', token: 't2', result: run(t2) })\nresults.push({ step: 'verify(t2) again', token: 't2', result: run(t2) })\nresults.push({ step: 'verify(t1) again', token: 't1', result: run(t1) })\n\nconst evidence = {\n  title: 'fast-jwt cache confusion when cacheKeyBuilder collisions occur',\n  environment: {\n    node: process.version,\n    fastJwt: require('./package.json').version\n  },\n  config: {\n    cache: true,\n    cacheTTL: 60000,\n    cacheKeyBuilder: \"returns constant key 'aud=admin' (realistic collision pattern)\"\n  },\n  tokens: {\n    t1: { claims: { sub: 'userA', aud: 'admin' }, jwt: t1 },\n    t2: { claims: { sub: 'userB', aud: 'admin' }, jwt: t2 }\n  },\n  observed: results\n}\n\nconst outPath = path.join(process.cwd(), 'evidence-cache-keybuilder-confusion.json')\nfs.writeFileSync(outPath, JSON.stringify(evidence, null, 2))\nconsole.log('Wrote evidence to:', outPath)\n\nfor (const r of results) {\n  console.log(r.step, '=>', r.result.ok ? `PASS sub=${r.result.sub}` : `FAIL ${r.result.code}`)\n}\n\nOutput:\nPS C:\\Users\\Franciny Rojas\\Desktop\\crypto-research\\fast-jwt> node poc_cache_keybuilder_confusion_evidence.js\nWrote evidence to: C:\\Users\\Franciny Rojas\\Desktop\\crypto-research\\fast-jwt\\evidence-cache-keybuilder-confusion.json\nverify(t1) => PASS sub=userA\nverify(t2) => PASS sub=userA\nverify(t2) again => PASS sub=userA\nverify(t1) again => PASS sub=userA\nPS C:\\Users\\Franciny Rojas\\Desktop\\crypto-research\\fast-jwt>","state":0,"publishedAt":"2026-04-03T04:07:09+00:00","modifiedAt":"2026-04-08T12:02:30.353217+00:00","affected":[{"ecosystem":"npm","name":"fast-jwt","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"0.0.1"},{"kind":"fixed","version":"6.2.0"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[{"system":"CVSS_V3","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N","score":null,"severity":null},{"system":"Publisher rating","vector":null,"score":null,"severity":"CRITICAL"}],"references":["https://github.com/nearform/fast-jwt/security/advisories/GHSA-rp9m-7r4c-75qg","https://nvd.nist.gov/vuln/detail/CVE-2026-35039","https://github.com/nearform/fast-jwt/commit/de121056c6415b58770c60640881eaec67ac4ceb","https://github.com/nearform/fast-jwt"],"sourceUrl":"https://github.com/advisories/GHSA-rp9m-7r4c-75qg","license":"GitHub Advisory Database · CC-BY-4.0","key":"osv:GHSA-rp9m-7r4c-75qg","identifiers":["GHSA-rp9m-7r4c-75qg","CVE-2026-35039"]},"retained":false,"historicalAffected":[]}],"coverage":[{"source":"cisa-kev:global","scope":"Complete CISA KEV catalog; all vendors. Missing entries do not establish absence of exploitation.","generation":"2026-10-26T08:22:31.5744378+00:00","complete":true,"records":1756,"lastAttemptAt":"2026-10-26T08:22:31.5744378+00:00","lastSuccessAt":"2026-10-26T08:22:31.7460185+00:00","error":null,"lastVerifiedAt":"2026-10-26T08:22:31.7460215+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null},{"source":"cve-program","scope":"Official CVE Program baseline and cumulative hourly delta; all vendors, published and rejected records","generation":"cve-5ba1945314e042c2ced14b33f3680fa71692812d925544312b218f5eb8a26e71-7be668fdfdeaeb5567583afe06ddb6baaaf3c46d3179c97a67bbd53e37799306","complete":true,"records":414096,"lastAttemptAt":"2026-10-26T08:20:52.7258433+00:00","lastSuccessAt":"2026-10-26T08:20:52.6822499+00:00","error":null,"lastVerifiedAt":"2026-10-26T08:20:52.7258433+00:00","snapshotPublishedAt":"2026-10-26T07:44:21+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null},{"source":"euvd","scope":"Rolling EUVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global EUVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-43321610382942b8ad8c85917eab1546","complete":false,"records":4735,"lastAttemptAt":"2026-10-26T08:22:34.3793835+00:00","lastSuccessAt":"2026-10-24T23:18:49.862+00:00","error":"InvalidDataException","lastVerifiedAt":"2026-10-24T23:18:49.862+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":4764,"expectedIdentifiers":13474},{"source":"first-epss:nuget","scope":"FIRST EPSS for explicit CVE aliases in the collected public NuGet archive. Not a global EPSS backfill.","generation":"2026-10-26T08:22:31.5744378+00:00","complete":true,"records":1093,"lastAttemptAt":"2026-10-26T08:22:31.5744378+00:00","lastSuccessAt":"2026-10-26T08:22:31.7471397+00:00","error":null,"lastVerifiedAt":"2026-10-26T08:22:31.7471404+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null},{"source":"nvd","scope":"Rolling NVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global NVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-204a3fb93a00487e8f7f36db01dc851d","complete":true,"records":13413,"lastAttemptAt":"2026-10-26T08:22:32.3026766+00:00","lastSuccessAt":"2026-10-25T16:19:28.681+00:00","error":null,"lastVerifiedAt":"2026-10-25T16:19:28.681+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13474,"expectedIdentifiers":13474},{"source":"osv:Go","scope":"Complete OSV Go ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"go-2375a4e597279abf92e8747e140fe8da9d421877cd49cae3ac69a2cb78469b2c","complete":true,"records":9779,"lastAttemptAt":"2026-10-26T08:22:31.5153923+00:00","lastSuccessAt":"2026-10-24T13:19:53.1346702+00:00","error":null,"lastVerifiedAt":"2026-10-26T08:22:31.5153923+00:00","snapshotPublishedAt":"2026-10-24T03:03:12+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null},{"source":"osv:NuGet","scope":"Complete OSV NuGet ecosystem archive, including GitHub Advisory Database records; NuGet version matching only","generation":"nuget-16ad89dfea13ab9e9b0108e63a759d63bd4559cc68b58491107d62ee35bbbaf2","complete":true,"records":1965,"lastAttemptAt":"2026-10-26T08:20:52.7776084+00:00","lastSuccessAt":"2026-10-23T16:01:26.4487605+00:00","error":null,"lastVerifiedAt":"2026-10-26T08:20:52.7776084+00:00","snapshotPublishedAt":"2026-10-21T12:18:07+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null},{"source":"osv:crates.io","scope":"Complete OSV crates.io ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"crates.io-7da67a15aa6e56a0e3081a7646dc74d537e6ef974afc593c2b1794ed01970648","complete":true,"records":2969,"lastAttemptAt":"2026-10-26T08:22:31.531764+00:00","lastSuccessAt":"2026-10-24T13:19:59.1145547+00:00","error":null,"lastVerifiedAt":"2026-10-26T08:22:31.531764+00:00","snapshotPublishedAt":"2026-10-24T08:47:51+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null},{"source":"osv:npm","scope":"Complete OSV npm ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"npm-654e90fb44abcd729ddecf56d54c1b46ce87ca3181850e8bc65d5c8befceb659","complete":true,"records":230805,"lastAttemptAt":"2026-10-26T08:22:31.3932452+00:00","lastSuccessAt":"2026-10-26T08:22:30.1390577+00:00","error":null,"lastVerifiedAt":"2026-10-26T08:22:31.3932452+00:00","snapshotPublishedAt":"2026-10-26T07:48:28+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null}]}