python3-pyjwt: Fix CVE-2026-48526

Reject JSON Web Key documents passed directly as HMAC secrets. This
prevents public asymmetric JWK data from being reused as an HMAC key
when an application permits mixed symmetric and asymmetric algorithms.

This patch applies the relevant subset of the upstream 2.13.0 fix.
The upstream commit is referenced in [1], and the public advisory is
referenced in [2].

[1] https://github.com/jpadilla/pyjwt/commit/95791b1759b8aa4f2203575d344d5c78564cdc81
[2] https://github.com/advisories/GHSA-xgmm-8j9v-c9wx

Signed-off-by: Hetvi Thakar <hthakar@cisco.com>
Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
This commit is contained in:
Hetvi Thakar
2026-08-05 22:51:00 -07:00
committed by Anuj Mittal
parent b9877579dc
commit 3c9dd88058
2 changed files with 88 additions and 0 deletions
@@ -0,0 +1,87 @@
From 9d2064bccc0ac60884906d9dd89ace589f9f8281 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jos=C3=A9=20Padilla?= <jpadilla@users.noreply.github.com>
Date: Mon, 3 Aug 2026 03:22:08 -0700
Subject: [PATCH] algorithms: reject raw JWK documents as HMAC secrets
Reject JSON Web Key documents passed directly to
HMACAlgorithm.prepare_key. Public asymmetric JWK data must not be
accepted as an HMAC secret when callers permit mixed algorithm families.
CVE: CVE-2026-48526
Upstream-Status: Backport [https://github.com/jpadilla/pyjwt/commit/95791b1759b8aa4f2203575d344d5c78564cdc81]
Backport Changes:
- Extracted only the CVE-2026-48526 raw-JWK rejection and regression tests
from the bundled upstream 2.13.0 commit. The other requested CVE fixes
are carried as separate patches.
- Adapted the hunk and test locations to PyJWT 2.8.0, renamed the upstream
local variable `jwk_obj` to `jwk`, and omitted explanatory comments;
the validation logic and assertions are unchanged.
- Excluded the separate empty-HMAC-key hardening bundled in the same file.
- Omitted the 2.13.0 version and changelog updates, CVE-2026-48523 (which
does not affect 2.8.0), and unrelated hardening from the bundled commit.
(cherry picked from commit 95791b1759b8aa4f2203575d344d5c78564cdc81)
Signed-off-by: Hetvi Thakar <hthakar@cisco.com>
---
jwt/algorithms.py | 13 +++++++++++++
tests/test_algorithms.py | 22 ++++++++++++++++++++++
2 files changed, 35 insertions(+)
diff --git a/jwt/algorithms.py b/jwt/algorithms.py
index ed18715..b6303ed 100644
--- a/jwt/algorithms.py
+++ b/jwt/algorithms.py
@@ -270,6 +270,19 @@ class HMACAlgorithm(Algorithm):
" should not be used as an HMAC secret."
)
+ stripped = key_bytes.lstrip()
+ if stripped.startswith(b"{"):
+ try:
+ jwk = json.loads(key_bytes)
+ except ValueError:
+ jwk = None
+ if isinstance(jwk, dict) and "kty" in jwk:
+ raise InvalidKeyError(
+ "The specified key looks like a JWK and should not be "
+ "used directly as an HMAC secret. Load it via "
+ "PyJWK / HMACAlgorithm.from_jwk first."
+ )
+
return key_bytes
@overload
diff --git a/tests/test_algorithms.py b/tests/test_algorithms.py
index 1a39552..e5220c6 100644
--- a/tests/test_algorithms.py
+++ b/tests/test_algorithms.py
@@ -108,6 +108,28 @@ class TestAlgorithms:
with pytest.raises(InvalidKeyError):
algo.from_jwk(keyfile.read())
+ @pytest.mark.parametrize(
+ "jwk_file",
+ [
+ "jwk_rsa_pub.json",
+ "jwk_ec_pub_P-256.json",
+ "jwk_okp_pub_Ed25519.json",
+ "jwk_hmac.json",
+ ],
+ )
+ def test_hmac_prepare_key_rejects_jwk_json(self, jwk_file: str) -> None:
+ algo = HMACAlgorithm(HMACAlgorithm.SHA256)
+
+ with open(key_path(jwk_file)) as keyfile:
+ with pytest.raises(InvalidKeyError, match="looks like a JWK"):
+ algo.prepare_key(keyfile.read())
+
+ def test_hmac_prepare_key_accepts_json_without_kty(self) -> None:
+ algo = HMACAlgorithm(HMACAlgorithm.SHA256)
+
+ key = algo.prepare_key('{"this": "is just a json-shaped secret"}')
+ assert key == b'{"this": "is just a json-shaped secret"}'
+
@crypto_required
def test_rsa_should_parse_pem_public_key(self):
algo = RSAAlgorithm(RSAAlgorithm.SHA256)
@@ -10,6 +10,7 @@ SRC_URI += " \
file://CVE-2026-48522.patch \
file://CVE-2026-48524.patch \
file://CVE-2026-48525.patch \
file://CVE-2026-48526.patch \
"
SRC_URI[sha256sum] = "57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de"