From 5764389f3fe00b1e291ceb157499be310f29dda7 Mon Sep 17 00:00:00 2001 From: Keaton Neville Date: Tue, 21 May 2024 15:10:53 -0400 Subject: [PATCH 1/2] fix vuln --- jose/constants.py | 2 ++ jose/jwe.py | 20 ++++++++++++++------ tests/test_jwe.py | 35 +++++++++++++++++++++++++++++++++-- 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/jose/constants.py b/jose/constants.py index ab4d74d3..c6951daf 100644 --- a/jose/constants.py +++ b/jose/constants.py @@ -96,3 +96,5 @@ class Zips: ZIPS = Zips() +JWE_SIZE_LIMIT = 250 * 1024 + diff --git a/jose/jwe.py b/jose/jwe.py index 2c387ff4..377b30aa 100644 --- a/jose/jwe.py +++ b/jose/jwe.py @@ -6,7 +6,7 @@ from . import jwk from .backends import get_random_bytes -from .constants import ALGORITHMS, ZIPS +from .constants import ALGORITHMS, ZIPS, JWE_SIZE_LIMIT from .exceptions import JWEError, JWEParseError from .utils import base64url_decode, base64url_encode, ensure_binary @@ -78,6 +78,10 @@ def decrypt(jwe_str, key): """ header, encoded_header, encrypted_key, iv, cipher_text, auth_tag = _jwe_compact_deserialize(jwe_str) + # Addresses CWE-2024-33664 + if len(jwe_str) > JWE_SIZE_LIMIT: + raise JWEError(f"JWE size exceeds {JWE_SIZE_LIMIT} bytes") + # Verify that the implementation understands and can process all # fields that it is required to support, whether required by this # specification, by the algorithms being used, or by the "crit" @@ -424,13 +428,13 @@ def _compress(zip, plaintext): (bytes): Compressed plaintext """ if zip not in ZIPS.SUPPORTED: - raise NotImplementedError("ZIP {} is not supported!") + raise NotImplementedError(f"ZIP {zip} is not supported!") if zip is None: compressed = plaintext elif zip == ZIPS.DEF: compressed = zlib.compress(plaintext) else: - raise NotImplementedError("ZIP {} is not implemented!") + raise NotImplementedError(f"ZIP {zip} is not implemented!") return compressed @@ -446,13 +450,17 @@ def _decompress(zip, compressed): (bytes): Compressed plaintext """ if zip not in ZIPS.SUPPORTED: - raise NotImplementedError("ZIP {} is not supported!") + raise NotImplementedError(f"ZIP {zip} is not supported!") if zip is None: decompressed = compressed elif zip == ZIPS.DEF: - decompressed = zlib.decompress(compressed) + # Addresses CWE-2024-33664 + decompressor = zlib.decompressobj() + decompressed = decompressor.decompress(compressed, max_length=JWE_SIZE_LIMIT) + if decompressor.unconsumed_tail: + raise JWEError(f"Decompressed data exceeds {JWE_SIZE_LIMIT} bytes") else: - raise NotImplementedError("ZIP {} is not implemented!") + raise NotImplementedError(f"ZIP {zip} is not implemented!") return decompressed diff --git a/tests/test_jwe.py b/tests/test_jwe.py index f089d565..da948a2e 100644 --- a/tests/test_jwe.py +++ b/tests/test_jwe.py @@ -4,8 +4,8 @@ import jose.backends from jose import jwe -from jose.constants import ALGORITHMS, ZIPS -from jose.exceptions import JWEParseError +from jose.constants import ALGORITHMS, ZIPS, JWE_SIZE_LIMIT +from jose.exceptions import JWEParseError, JWEError from jose.jwk import AESKey, RSAKey from jose.utils import base64url_decode @@ -525,3 +525,34 @@ def test_kid_header_not_present_when_not_provided(self): encrypted = jwe.encrypt("Text", PUBLIC_KEY_PEM, enc, alg) header = json.loads(base64url_decode(encrypted.split(b".")[0])) assert "kid" not in header + + @pytest.mark.skipif(AESKey is None, reason="No AES backend") + def test_jwe_with_excessive_data(self): + enc = ALGORITHMS.A256CBC_HS512 + alg = ALGORITHMS.RSA_OAEP_256 + import jose.constants + old_limit = jose.constants.JWE_SIZE_LIMIT + try: + jose.constants.JWE_SIZE_LIMIT = 1024 + encrypted = jwe.encrypt(b"Text"*64*1024, PUBLIC_KEY_PEM, enc, alg) + header = json.loads(base64url_decode(encrypted.split(b".")[0])) + with pytest.raises(JWEError): + actual = jwe.decrypt(encrypted, PRIVATE_KEY_PEM) + finally: + jose.constants.JWE_SIZE_LIMIT = old_limit + + @pytest.mark.skipif(AESKey is None, reason="No AES backend") + def test_jwe_zip_with_excessive_data(self): + enc = ALGORITHMS.A256CBC_HS512 + alg = ALGORITHMS.RSA_OAEP_256 + import jose.constants + old_limit = jose.constants.JWE_SIZE_LIMIT + try: + jose.constants.JWE_SIZE_LIMIT = 1024 + encrypted = jwe.encrypt(b"Text"*64*1024, PUBLIC_KEY_PEM, enc, alg, zip=ZIPS.DEF) + assert len(encrypted) < jose.constants.JWE_SIZE_LIMIT + header = json.loads(base64url_decode(encrypted.split(b".")[0])) + with pytest.raises(JWEError): + actual = jwe.decrypt(encrypted, PRIVATE_KEY_PEM) + finally: + jose.constants.JWE_SIZE_LIMIT = old_limit \ No newline at end of file From e341f90efaaa9ba41ac4ce83f74ab936d5430378 Mon Sep 17 00:00:00 2001 From: Keaton Neville Date: Tue, 21 May 2024 15:12:01 -0400 Subject: [PATCH 2/2] newline --- tests/test_jwe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_jwe.py b/tests/test_jwe.py index da948a2e..b7780877 100644 --- a/tests/test_jwe.py +++ b/tests/test_jwe.py @@ -555,4 +555,4 @@ def test_jwe_zip_with_excessive_data(self): with pytest.raises(JWEError): actual = jwe.decrypt(encrypted, PRIVATE_KEY_PEM) finally: - jose.constants.JWE_SIZE_LIMIT = old_limit \ No newline at end of file + jose.constants.JWE_SIZE_LIMIT = old_limit