mirror of
https://github.com/openembedded/meta-openembedded.git
synced 2026-08-25 05:21:31 +00:00
python3-pyjwt: Fix CVE-2026-48525
Reject a non-empty compact payload segment for b64=false tokens before Base64URL decoding. The segment is unused for detached JWS verification, so decoding it allowed unauthenticated CPU and memory consumption. 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-w7vc-732c-9m39 Signed-off-by: Hetvi Thakar <hthakar@cisco.com> Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
This commit is contained in:
committed by
Anuj Mittal
parent
fffe550dfc
commit
b9877579dc
@@ -0,0 +1,115 @@
|
||||
From 91ac94bdc85d24c6d35a5f6cdd58eb8c6c4df0f0 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:21:30 -0700
|
||||
Subject: [PATCH] api_jws: reject non-empty detached payload segments
|
||||
|
||||
For b64=false tokens, reject a non-empty compact payload segment before
|
||||
Base64URL decoding. The segment is unused when detached_payload is
|
||||
supplied, so decoding attacker-controlled data only consumes CPU and
|
||||
memory.
|
||||
|
||||
CVE: CVE-2026-48525
|
||||
Upstream-Status: Backport [https://github.com/jpadilla/pyjwt/commit/95791b1759b8aa4f2203575d344d5c78564cdc81]
|
||||
|
||||
Backport Changes:
|
||||
- Extracted only the CVE-2026-48525 detached-payload segment check and
|
||||
regression test 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, used module-level
|
||||
hashlib and hmac imports, and shortened explanatory comments without
|
||||
changing the tested behavior.
|
||||
- Excluded the separate RFC 7797 b64/crit hardening bundled upstream.
|
||||
- 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/api_jws.py | 17 +++++++++++++----
|
||||
tests/test_api_jws.py | 34 +++++++++++++++++++++++++++++++++-
|
||||
2 files changed, 46 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/jwt/api_jws.py b/jwt/api_jws.py
|
||||
index 1750442..0d5ab2b 100644
|
||||
--- a/jwt/api_jws.py
|
||||
+++ b/jwt/api_jws.py
|
||||
@@ -274,10 +274,19 @@ class PyJWS:
|
||||
if not isinstance(header, dict):
|
||||
raise DecodeError("Invalid header string: must be a json object")
|
||||
|
||||
- try:
|
||||
- payload = base64url_decode(payload_segment)
|
||||
- except (TypeError, binascii.Error) as err:
|
||||
- raise DecodeError("Invalid payload padding") from err
|
||||
+ if header.get("b64", True) is False:
|
||||
+ # Detached compact serialization requires an empty payload
|
||||
+ # segment. Reject it before decoding attacker-controlled data.
|
||||
+ if payload_segment:
|
||||
+ raise DecodeError(
|
||||
+ "Payload segment must be empty when 'b64' is false."
|
||||
+ )
|
||||
+ payload = b""
|
||||
+ else:
|
||||
+ try:
|
||||
+ payload = base64url_decode(payload_segment)
|
||||
+ except (TypeError, binascii.Error) as err:
|
||||
+ raise DecodeError("Invalid payload padding") from err
|
||||
|
||||
try:
|
||||
signature = base64url_decode(crypto_segment)
|
||||
diff --git a/tests/test_api_jws.py b/tests/test_api_jws.py
|
||||
index 434874b..29f84a7 100644
|
||||
--- a/tests/test_api_jws.py
|
||||
+++ b/tests/test_api_jws.py
|
||||
@@ -1,3 +1,5 @@
|
||||
+import hashlib
|
||||
+import hmac
|
||||
import json
|
||||
from decimal import Decimal
|
||||
|
||||
@@ -11,7 +13,7 @@ from jwt.exceptions import (
|
||||
InvalidSignatureError,
|
||||
InvalidTokenError,
|
||||
)
|
||||
-from jwt.utils import base64url_decode
|
||||
+from jwt.utils import base64url_decode, base64url_encode
|
||||
from jwt.warnings import RemovedInPyjwt3Warning
|
||||
|
||||
from .utils import crypto_required, key_path, no_crypto_required
|
||||
@@ -766,6 +768,36 @@ class TestJWS:
|
||||
assert "b64" not in msg_header_obj
|
||||
assert msg_payload
|
||||
|
||||
+ def test_decode_b64_false_rejects_non_empty_payload_segment(
|
||||
+ self, jws: PyJWS, payload: bytes
|
||||
+ ) -> None:
|
||||
+ secret = "secret"
|
||||
+ header = {
|
||||
+ "typ": "JWT",
|
||||
+ "alg": "HS256",
|
||||
+ "b64": False,
|
||||
+ "crit": ["b64"],
|
||||
+ }
|
||||
+ encoded_header = base64url_encode(
|
||||
+ json.dumps(header, separators=(",", ":")).encode()
|
||||
+ )
|
||||
+ attacker_segment = b"A" * 1024
|
||||
+ signing_input = b".".join([encoded_header, payload])
|
||||
+ signature = hmac.new(
|
||||
+ secret.encode(), signing_input, hashlib.sha256
|
||||
+ ).digest()
|
||||
+ token = b".".join(
|
||||
+ [encoded_header, attacker_segment, base64url_encode(signature)]
|
||||
+ ).decode()
|
||||
+
|
||||
+ with pytest.raises(DecodeError, match="Payload segment must be empty"):
|
||||
+ jws.decode(
|
||||
+ token,
|
||||
+ secret,
|
||||
+ algorithms=["HS256"],
|
||||
+ detached_payload=payload,
|
||||
+ )
|
||||
+
|
||||
def test_decode_detached_content_without_proper_argument(self, jws):
|
||||
example_jws = (
|
||||
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImI2NCI6ZmFsc2V9"
|
||||
@@ -9,6 +9,7 @@ SRC_URI += " \
|
||||
file://CVE-2026-32597.patch \
|
||||
file://CVE-2026-48522.patch \
|
||||
file://CVE-2026-48524.patch \
|
||||
file://CVE-2026-48525.patch \
|
||||
"
|
||||
SRC_URI[sha256sum] = "57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user