Open
reject unsupported b64 crit and fix CVE-2026-48526 #50
reject unsupported b64 crit and fix CVE-2026-48526 #50
Open
lipengyu001Add on Jul 21
Total: 3 files changed+207-2
@@ -0,0 +1,93 @@
1+From 8fd2db6f6f2f5a26a71c3f5a3d2c6b57d801a7ce Mon Sep 17 00:00:00 2001
2+From: Codex <codex@localhost>
3+Date: Tue, 21 Jul 2026 00:00:00 +0800
4+Subject: [PATCH] Reject b64 critical header on PyJWT 1.7.1
5+ 
6+PyJWT 1.7.1 does not implement RFC 7797 b64=false / detached payload
7+handling, so it must not advertise b64 as a supported critical header
8+extension. Treat all crit entries as unsupported in this backport.
9+---
10+ jwt/api_jws.py | 5 +++--
11+ tests/test_api_jws.py | 28 ++++++++++++++++++++++++++++
12+ tests/test_api_jwt.py | 17 +++++++++++++++++
13+ 3 files changed, 48 insertions(+), 2 deletions(-)
14+ 
15+diff --git a/jwt/api_jws.py b/jwt/api_jws.py
16+index 876ada8..04d855b 100644
17+--- a/jwt/api_jws.py
18++++ b/jwt/api_jws.py
19+@@ -21,8 +21,9 @@ from .utils import base64url_decode, base64url_encode, force_bytes, merge_dict
20+ class PyJWS(object):
21+ header_typ = 'JWT'
22+
23+- # Extensions that PyJWT actually understands and supports
24+- _supported_crit = set(['b64'])
25++ # Extensions that PyJWT actually understands and supports.
26++ # PyJWT 1.7.1 does not implement RFC 7797 b64=false handling.
27++ _supported_crit = set()
28+
29+ def __init__(self, algorithms=None, options=None):
30+ self._algorithms = get_default_algorithms()
31+diff --git a/tests/test_api_jws.py b/tests/test_api_jws.py
32+index 26899b2..c76574d 100644
33+--- a/tests/test_api_jws.py
34++++ b/tests/test_api_jws.py
35+@@ -679,3 +679,31 @@ class TestJWS:
36+ jws.encode(payload, 'secret', headers={'kid': None})
37+
38+ assert 'Key ID header parameter must be a string' == str(exc.value)
39++
40++ def test_decode_rejects_b64_crit_extension(self, jws, payload):
41++ secret = 'secret'
42++ token = jws.encode(
43++ payload,
44++ secret,
45++ algorithm='HS256',
46++ headers={'crit': ['b64'], 'b64': False}
47++ )
48++
49++ with pytest.raises(InvalidTokenError) as exc:
50++ jws.decode(token, secret, algorithms=['HS256'])
51++
52++ assert 'Unsupported critical extension: b64' == str(exc.value)
53++
54++ def test_get_unverified_header_rejects_b64_crit_extension(self, jws, payload):
55++ secret = 'secret'
56++ token = jws.encode(
57++ payload,
58++ secret,
59++ algorithm='HS256',
60++ headers={'crit': ['b64'], 'b64': False}
61++ )
62++
63++ with pytest.raises(InvalidTokenError) as exc:
64++ jws.get_unverified_header(token)
65++
66++ assert 'Unsupported critical extension: b64' == str(exc.value)
67+diff --git a/tests/test_api_jwt.py b/tests/test_api_jwt.py
68+index 01ca656..67cbdaa 100644
69+--- a/tests/test_api_jwt.py
70++++ b/tests/test_api_jwt.py
71+@@ -520,3 +520,20 @@ class TestJWT:
72+ pass
73+ else:
74+ assert False, "Unexpected DeprecationWarning raised."
75++
76++ def test_decode_rejects_b64_crit_extension(self, jwt):
77++ from jwt.exceptions import InvalidTokenError
78++
79++ secret = 'secret'
80++ payload = {'sub': 'attacker', 'role': 'admin'}
81++ token = jwt.encode(
82++ payload,
83++ secret,
84++ algorithm='HS256',
85++ headers={'crit': ['b64'], 'b64': False}
86++ )
87++
88++ with pytest.raises(InvalidTokenError) as exc:
89++ jwt.decode(token, secret, algorithms=['HS256'])
90++
91++ assert 'Unsupported critical extension: b64' == str(exc.value)
92+--
93+1.8.3.1
@@ -0,0 +1,103 @@
1+From 95791b1759b8aa4f2203575d344d5c78564cdc81 Mon Sep 17 00:00:00 2001
2+From: =?UTF-8?q?Jos=C3=A9=20Padilla?= <jpadilla@webapplicate.com>
3+Date: Thu, 21 May 2026 12:00:00 -0400
4+Subject: [PATCH] Backport CVE-2026-48526 fix to PyJWT 1.7.1
5+MIME-Version: 1.0
6+Content-Type: text/plain; charset=UTF-8
7+Content-Transfer-Encoding: 8bit
8+ 
9+Reject JWK JSON documents passed as raw HMAC secrets in
10+HMACAlgorithm.prepare_key to close an algorithm-confusion gap that the
11+existing PEM/SSH guard does not cover.
12+ 
13+Adapted for PyJWT 1.7.1. The upstream fix is part of the 2.13.0 security
14+bundle; only the CVE-2026-48526 HMAC/JWK guard is backported here.
15+---
16+ jwt/algorithms.py | 22 ++++++++++++++++++++++
17+ tests/test_advisory.py | 45 +++++++++++++++++++++++++++++++++++++++++++++
18+ 2 files changed, 67 insertions(+), 1 deletion(-)
19+ 
20+diff --git a/jwt/algorithms.py b/jwt/algorithms.py
21+index 8e4d739..3c39efb 100644
22+--- a/jwt/algorithms.py
23++++ b/jwt/algorithms.py
24+@@ -147,1 +147,22 @@ class HMACAlgorithm(Algorithm):
25+- return key
26++ # Defense against algorithm-confusion attacks: an attacker with
27++ # control over the token header can force this code path by setting
28++ # alg=HS*, and HMACAlgorithm is the only algorithm that accepts
29++ # arbitrary bytes as a valid secret. Other algorithms reject
30++ # non-key-shaped input naturally. Even a symmetric (kty=oct) JWK
31++ # should be loaded via HMACAlgorithm.from_jwk rather than fed as raw
32++ # JSON bytes (whose contents are not the secret material).
33++ stripped = key.lstrip()
34++ if stripped.startswith(b'{'):
35++ try:
36++ jwk_obj = json.loads(key)
37++ except ValueError:
38++ jwk_obj = None
39++
40++ if isinstance(jwk_obj, dict) and 'kty' in jwk_obj:
41++ raise InvalidKeyError(
42++ 'The specified key looks like a JWK and should not be '
43++ 'used directly as an HMAC secret. Load it via '
44++ 'HMACAlgorithm.from_jwk first.'
45++ )
46++
47++ return key
48+diff --git a/tests/test_advisory.py b/tests/test_advisory.py
49+index e8065d4..b261430 100644
50+--- a/tests/test_advisory.py
51++++ b/tests/test_advisory.py
52+@@ -107,3 +107,48 @@ class TestAdvisory:
53+ ssh_key_bytes,
54+ algorithms=jwt.algorithms.get_default_algorithms()
55+ )
56++
57++ def test_ghsa_xgmm_8j9v_c9wx(self):
58++ import hashlib
59++ import hmac
60++ import json
61++ from jwt.utils import base64url_encode
62++
63++ # CVE-2026-48526: public-key JWK accepted as HMAC secret could allow
64++ # forged HS256 tokens when callers mix symmetric and asymmetric
65++ # algorithms and pass raw JSON JWK text as the verification key.
66++ rsa_jwk_json = json.dumps({
67++ 'kty': 'RSA',
68++ 'n': 'AQAB',
69++ 'e': 'AQAB'
70++ })
71++
72++ signing_input = (
73++ b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.'
74++ b'eyJzdWIiOiJhbGljZSIsImFkbWluIjp0cnVlfQ'
75++ )
76++ signature = hmac.new(
77++ rsa_jwk_json.encode('utf-8'),
78++ signing_input,
79++ hashlib.sha256
80++ ).digest()
81++ encoded_bad = b'.'.join((
82++ signing_input,
83++ base64url_encode(signature)
84++ ))
85++
86++ with pytest.raises(InvalidKeyError):
87++ jwt.decode(
88++ encoded_bad,
89++ rsa_jwk_json,
90++ algorithms=jwt.algorithms.get_default_algorithms()
91++ )
92++
93++ def test_hmac_should_reject_symmetric_jwk_as_raw_secret(self):
94++ oct_jwk_json = json.dumps({'kty': 'oct', 'k': 'c2VjcmV0'})
95++
96++ with pytest.raises(InvalidKeyError):
97++ jwt.decode(
98++ jwt.encode({'test': 1234}, oct_jwk_json, algorithm='HS256'),
99++ oct_jwk_json,
100++ algorithms=['HS256']
101++ )
102+--
103+1.8.3.1
@@ -2,7 +2,7 @@
2%bcond_with tests2%bcond_with tests
3Name: python-jwt3Name: python-jwt
4Version: 1.7.14Version: 1.7.1
5-Release: 55+Release: 6
6Summary: JSON Web Token implementation in Python6Summary: JSON Web Token implementation in Python
7License: MIT7License: MIT
8URL: https://github.com/jpadilla/pyjwt8URL: https://github.com/jpadilla/pyjwt
@@ -10,7 +10,9 @@ Source0: https://files.pythonhosted.org/packages/source/P/PyJWT/PyJWT
10BuildArch: noarch10BuildArch: noarch
11 11 
12Patch6000: backport-CVE-2022-29217.patch12Patch6000: backport-CVE-2022-29217.patch
13-Patch6001: backport-CVE-2026-32597-1.patch13+Patch6001: backport-CVE-2026-32597-1.patch
14+Patch6002: backport-CVE-2026-32597-2.patch
15+Patch6003: backport-CVE-2026-48526.patch
14 16 
15BuildRequires: python2-devel python2-setuptools python2-cryptography17BuildRequires: python2-devel python2-setuptools python2-cryptography
16%if %{with tests}18%if %{with tests}
@@ -72,6 +74,13 @@ Requires: python%{python3_pkgversion}-cryptography
72%doc README.rst74%doc README.rst
73 75 
74%changelog76%changelog
77+* Tue Jul 21 2026 lipengyu <lipengyu@kylinos> - 1.7.1-6
78+- Type:CVE
79+- CVE:CVE-2026-32597 CVE-2026-48526
80+- SUG:NA
81+- DESC:reject unsupported b64 crit and fix CVE-2026-48526
82+ 
83+ 
75* Fri Jul 17 2026 xiaoo_robot <xiaoo_robot@petalmail.com> - 1.7.1-584* Fri Jul 17 2026 xiaoo_robot <xiaoo_robot@petalmail.com> - 1.7.1-5
76- Type:CVE85- Type:CVE
77- CVE:CVE-2026-3259786- CVE:CVE-2026-32597