🛡️ CVE-2026-48523 — pyjwt

🟡 CVSS 5.4 — Medium ⚠️ Exploit Public CWE-347 NVD
5.4
CVSS Score
0 Low4 Medium7 High9 Critical10

Description

PyJWT: Algorithm allow-list bypass when decoding with PyJWK / PyJWKClient keys

> [!NOTE]

> Scored assuming a deployment where algorithm policy functions as an authentication/authorization boundary. In deployments where the algorithm policy enforces crypto agility only, the practical confidentiality impact is lower and the issue is closer to an integrity-of-policy-enforcement bug.

PyJWT 2.9.0 through 2.12.1 allows a verifier-side algorithm allow-list bypass when jwt.decode() or jwt.decode_complete() are called with a PyJWK key. The token header alg is checked against the caller-supplied algorithms allow-list, but signature verification is performed with the algorithm bound to the PyJWK object instead of the header algorithm. An attacker who controls a registered JWK/JWKS private key can sign with a disallowed algorithm, advertise an allowed algorithm in the JWT header, and still be accepted. The issue affects the documented PyJWKClient.get_signing_key_from_jwt(...) flow.

Summary

PyJWT's PyJWK verification path allows a verifier-side algorithm allow-list bypass.

In affected versions, when a JWT is decoded with a PyJWK object, PyJWT verifies that the header alg string is present in the caller's algorithms=[...] list, but it does not actually use the header algorithm to verify the signature. Instead, it verifies with the algorithm already bound to the PyJWK object.

This lets an attacker who controls a registered JWK/JWKS private key sign with a disallowed algorithm and have the token accepted as long as the JWT header advertises an allowed algorithm. This affects the documented PyJWKClient usage flow and does not require any non-default flags or unsafe configuration.

Details

In jwt/api_jws.py in 2.12.1, _verify_signature() treats PyJWK keys differently from normal PEM/public-key inputs:

```python

if algorithms is None and isinstance(key, PyJWK):

algorithms = [key.algorithm_name]

...

if not alg or (algorithms is not None and alg not in algorithms):

raise InvalidAlgorithmError("The specified alg value is not allowed")

if isinstance(key, PyJWK):

alg_obj = key.Algorithm

prepared_key = key.key

else:

alg_obj = self.get_algorithm_by_name(alg)

prepared_key = alg_obj.prepare_key(key)

```

This logic means:

1. The JWT header alg is checked only as a string against the caller-supplied allow-list.

2. If the key is a PyJWK, the actual verifier is not selected from the header algorithm.

3. Instead, PyJWT always verifies with key.Algorithm, which is fixed when the PyJWK object is created.

PyJWK binds its algorithm in jwt/api_jwk.py from the JWK's alg field or from key-type defaults:

```python

if not algorithm and isinstance(self._jwk_data, dict):

algorithm = self._jwk_data.get("alg", None)

...

self.algorithm_name = algorithm

self.Algorithm = get_default_algorithms()[algorithm]

self.key = self.Algorithm.from_jwk(self._jwk_data)

```

So once a PyJWK is constructed, the verifier uses the PyJWK's bound algorithm, not the JWT header algorithm.

The issue is reachable through the documented JWKS flow. In docs/usage.rst, the project documents:

```python

signing_key = jwks_client.get_signing_key_from_jwt(token)

jwt.decode(

token,

signing_key,

audience="https://expenses-api",

options={"verify_exp": False},

algorithms=["RS256"],

)

```

PyJWKClient.get_signing_key_from_jwt() returns a PyJWK, so this documented path is affected.

This is not a "no-key forgery" issue. The attacker still needs control of an accepted JWK/JWKS private key. However, that is realistic in deployments such as:

  • self-service OAuth client assertions
  • multi-tenant key registration
  • federation / BYO-JWKS trust models
  • any system where external parties sign JWTs with their own registered keys

In those cases, the attacker can bypass verifier-side algorithm policy. For example, if the server intends to only accept PS256, an attacker controlling an accepted RSA JWK can sign with RS256, set alg=PS256 in the JWT header, and still be accepted through the PyJWK path.

The same forged token is rejected through the normal PEM/public-key verification path, which shows the bug is specific to PyJWK verification rather than expected JWT behavior.

This behavior was introduced by commit ab8176abe21e550dbc1c9a6bb7e78ad80853bfb1 (Decode with PyJWK (#886)), which is present in tagged releases 2.9.0, 2.10.0, 2.10.1, 2.11.0, 2.12.0, and 2.12.1.

PoC

Tested locally against PyJWT 2.12.1 on Python 3.12.10 with cryptography 45.0.6.

Install dependencies:

```bash

python -m pip install pyjwt==2.12.1 cryptography

```

Run the following script:

```python

import json

import jwt

from cryptography.hazmat.primitives.asymmetric import rsa

from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat

from jwt.api_jwk import PyJWK

from jwt.algorithms import RSAAlgorithm

from jwt.utils import base64url

How this vulnerability can be exploited

This issue can be reached over the network, attack complexity is low, an attacker needs low-level privileges on the target. No user interaction is required. The scope is unchanged, so the impact stays within the vulnerable component. Rated impact: confidentiality low, integrity low, availability none.

Weakness class

CVE-2026-48523 is classified as CWE-347: Improper Verification of Cryptographic Signature. A signature is not checked correctly, so forged or modified content is accepted as genuine.

Affected software

CVE-2026-48523 is recorded against 2 packages.

  • pyjwt (from 2.9.0 up to 2.13.0)
  • unknown

Timeline and source

Published on 15 June 2026 and last revised on 17 June 2026. A public exploit is known to exist, which raises the urgency of patching considerably. Record sourced from NVD.

References

github.com (Web)
nvd.nist.gov (Advisory)
github.com (Package)
github.com (Web)

CVE-2026-48523 on other distributions

Each distribution ships its own build and its own fixed version. Pick the one you run:

Details

Severity MEDIUM
CVSS Score 5.4
CVSS Vector CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N
CWE CWE-347
Public Exploit ⚠️ Yes
Source NVD
Published 2026-06-15
Updated 2026-08-12
Modified 2026-06-17
Fix URL N/A

Affected Packages

Software From version Fixed in
pyjwt 2.9.0 2.13.0
unknown

Exploit Protection

Are you running pyjwt?

CVE-2026-48523 carries CVSS 5.4 Medium rating and a public exploit already exists. BotEraser checks your installation against this and other known CVE records, and blocks IPs associated with exploit activity.

Check My Site For CVE-2026-48523 →

No credit card required  ·  Results in minutes

ⓘ Data Notice: The information presented above has been compiled from publicly available internet sources. Boteraser aggregates this data solely for informational purposes and does not independently classify, evaluate, or endorse any findings about the vulnerabilities listed. The accuracy and completeness of this information is the sole responsibility of the original publishers. Boteraser and its operators accept no liability for any decisions made based on this data.