CVE-2026-102274: Malformed RSA JWK Aborts PyJWT JWK Set Parsing

Blog3 min read

SecDim Security Research reported CVE-2026-102274 to the PyJWT maintainer on 2026-09-05, and the vulnerability was subsequently fixed in PyJWT 2.14.0. A single malformed RSA key inside a JWK Set can abort parsing of the entire set. The exception stops parsing before any subsequent key is processed, including keys unrelated to the malformed entry.

The vulnerability

PyJWT’s PyJWKSet class provides the constructor that builds a JWK Set from a list of keys. It is designed to skip unusable keys and continue processing the rest of the set. Each key’s construction is wrapped in error handling that catches PyJWT’s own exception base class, PyJWTError, and skips forward on failure:

for key in keys:
    try:
        self.keys.append(PyJWK(key))
    except PyJWTError as error:
        if isinstance(error, MissingCryptographyError):
            raise error
        # skip unusable keys
        continue

That design only holds if every failure inside the loop actually raises a PyJWTError.

The initializer for an individual key, PyJWK, calls directly into the matching algorithm’s key-construction logic, with no exception handling of its own. For an RSA key, that logic is the RSA algorithm’s from_jwk function, responsible for reconstructing a usable key object from the fields present in the JWK.

Some RSA private JWKs include a set of precomputed values that let from_jwk reconstruct the key efficiently. When those values are absent, it instead has to recover the key’s prime factors from the public modulus and the private exponent, using a function from the underlying cryptography library.

The recovery only succeeds if the supplied private exponent is actually valid for the given modulus and public exponent. If it is not, cryptography cannot factor the modulus and raises a ValueError, a generic Python exception rather than one of PyJWT’s own types. Because ValueError is not a subclass of PyJWTError, the guard shown above does not catch it. The exception propagates straight out of the constructor and terminates the loop before any subsequent key gets processed.

Proof of concept

A JWK Set is constructed with two keys: one deliberately malformed, one valid. Only the malformed key should be skipped. Instead, neither key is added to the resulting set.

import jwt

good_jwk = {
    "kty": "RSA",
    "n": "<a valid base64url-encoded RSA modulus>",
    "e": "AQAB",
}

bad_jwk = {
    "kty": "RSA",
    "n": good_jwk["n"],
    "e": "AQAB",
    "d": "AAAAAA",  # not a valid private exponent for this modulus
}

jwt.PyJWKSet.from_dict({"keys": [bad_jwk, good_jwk]})
# raises: ValueError: Unable to compute factors p and q from exponent d.
# (uncaught, good_jwk is never added)

Impact

Exploitation requires a malformed key to be present in a JWK Set that the target application parses.

In a multi-tenant platform that lets each tenant register its own signing key, or a gateway that combines JWKS from several upstream identity providers into a single document, a single malformed key can be used to abort parsing of the entire set.
An adversary can register their own tenant’s signing key to inject a malformed key into the shared JWK Set, causing parsing to fail and disrupting token verification for all tenants relying on that set.

Every key in the affected set becomes unusable at once, so every legitimate token relying on any of those keys fails to validate until the malformed entry is removed or the library is patched.

Advisory timeline

  • 2026-09-05: SecDim Security Research reports the vulnerability to the PyJWT maintainer. Day 0 of a 90-day responsible disclosure window.
  • 2026-09-08: The maintainer reproduces the issue on PyJWT 2.13.0 and confirms the independently verified affected range, >= 2.9.0 through <= 2.13.0. A narrow fix is prepared; the advisory is published.
  • 2026-09-10: The fix lands on master (commit 8915570). The full test suite, linting, and type checks pass.
  • 2026-09-11: PyJWT 2.14.0 is released with the fix.
  • 2026-12-04: Scheduled full-disclosure deadline (Day 90), extendable by 14 days if a tagged release is imminent but not yet shipped.

Fix status

The fix shipped in PyJWT 2.14.0. PyJWK now converts key-construction ValueErrors into InvalidKeyError, a PyJWTError subclass. This allows the constructor’s existing skip logic to function as originally intended. Users running an affected version should upgrade to PyJWT 2.14.0 or later.

References

Find, hack and fix this vulnerability in: jwt.py sandbox

Questions or comments? Discuss this post on SecDim Community →

Try it yourself

Find, Hack and Fix Your First Vulnerability

Reading about security bugs is one thing — fixing one is how the skill sticks. Play a free challenge from the wargame, no setup required.