Aug 4, 2026·6 min read·85 visits
A vulnerability in the aiohttp WebSocket client allows malicious servers to bypass client configuration and force decompression of server-supplied payloads. This occurs because the parser improperly defaults to accepting compressed frames even when the permessage-deflate extension was not negotiated, allowing attackers to trigger a Denial-of-Service (DoS) condition via decompression bombs.
CVE-2026-59881 is a protocol compliance and input validation vulnerability in the client-side WebSocket implementation of the aiohttp asynchronous HTTP client/server framework for Python. Prior to version 3.14.2, the framework's parser unexpectedly accepts and attempts to decompress frames containing the RSV1 bit, even when the permessage-deflate extension has not been negotiated during the initial WebSocket handshake. This violation of RFC 6455 allows a malicious or compromised server to bypass client configuration, forcing decompression routines that can lead to high CPU and memory consumption, resulting in a denial-of-service condition.
CVE-2026-59881 is an input validation and protocol compliance vulnerability in the client-side WebSocket parsing architecture of the aiohttp framework. The defect exists within the frame-processing engine, which fails to correctly enforce RFC 6455 specifications governing the use of reserved bits. Specifically, the parser does not validate whether the RSV1 bit in incoming WebSocket headers has been mutually negotiated during the connection upgrade handshake.
According to RFC 6455, the RSV1 bit is reserved for extension negotiations, most notably the permessage-deflate compression protocol defined in RFC 7692. If this extension is not explicitly established, receiving a frame with RSV1 set to 1 represents an invalid state. The specification mandates that the receiver must fail the connection immediately with close code 1002 (Protocol Error) upon encountering this condition.
In affected versions of aiohttp, the client fails to reject these unauthorized frames. Instead, the frame parser accepts the payload and passes it directly to the internal zlib decompression routine. This defect exposes the client to unexpected, server-controlled decompression tasks, which can be leveraged by a malicious remote host to exhaust client system resources.
The root cause of CVE-2026-59881 lies in the default constructor configuration of the WebSocketReader class and its subsequent instantiation pattern. Located in aiohttp/_websocket/reader_py.py, the WebSocketReader is responsible for decoding bytes from the underlying TCP socket and assembling them into distinct WebSocket messages.
The class constructor signature was designed with a default value of True for its internal compress flag. This configuration assumed that decompression capability should be active unless explicitly deactivated by the caller. This default value created a fallback state that bypassed proper state verification during connection establishment.
During connection setup in aiohttp/client.py:_ws_connect, the client initiates the reader but omits the compress argument. Because of this omission, the initialized WebSocketReader operates with compress=True regardless of the negotiated handshake. When the client receives a frame with the RSV1 bit set, it reads the local _compress flag, finds it active, and executes decompression rather than throwing a Protocol Error as mandated by RFC 6455.
An analysis of the vulnerable code paths demonstrates the mechanics of the fallback error. In aiohttp/_websocket/reader_py.py, the vulnerable signature was declared as follows:
# Vulnerable implementation in aiohttp/_websocket/reader_py.py
def __init__(
self,
queue: WebSocketDataQueue,
max_msg_size: int,
compress: bool = True, # Defect: Defaults to True
decode_text: bool = True,
) -> None:
self._compress = compress
# ... parser state setup ...When the client-side connection helper _ws_connect initiated this parser, it did not explicitly supply the status of the compress variable derived from the handshake parameters:
# Vulnerable instantiation in aiohttp/client.py
# The local 'compress' variable is defined but not passed to the constructor
parser = WebSocketReader(reader, max_msg_size, decode_text=decode_text)The patch resolved this discrepancy by removing the default value from the WebSocketReader signature, rendering the configuration parameter mandatory:
# Patched implementation in aiohttp/_websocket/reader_py.py
def __init__(
self,
queue: WebSocketDataQueue,
max_msg_size: int,
compress: bool, # Fix: Default value removed
decode_text: bool,
) -> None:
self._compress = compressThe client upgrade routine was corrected to explicitly map the negotiated state to the reader initialization. This ensures that when compression is not negotiated, compress evaluates to False, forcing the reader to flag incoming RSV1 frames as protocol violations:
# Patched instantiation in aiohttp/client.py
parser = WebSocketReader(
reader,
max_msg_size,
compress=bool(compress), # Fix: Explicitly pass handshake setting
decode_text=decode_text,
)Exploitation of CVE-2026-59881 requires a malicious or compromised WebSocket server to which the vulnerable client initiates a connection. It is also possible for a Man-in-the-Middle (MitM) attacker on an unencrypted ws:// connection to intercept and inject the malicious frames into the stream.
The attack begins during the initial WebSocket HTTP upgrade handshake. The client initiates a connection, and the server accepts the upgrade but omits the Sec-WebSocket-Extensions: permessage-deflate header from the response. The client assumes the connection is established without compression support, yet due to the bug, its frame parser remains configured to accept and decompress frames.
Once the handshake is completed, the server transmits a crafted WebSocket frame where the RSV1 bit in the first byte is set to 1. The payload of this frame is structured as a valid zlib/deflate payload. Crucially, the payload is crafted as a decompression bomb (zip bomb), containing highly repetitive, highly compressible byte patterns designed to expand dramatically upon processing.
When the vulnerable client receives this frame, it passes the compressed bytes directly to zlib.decompress. The decompression process consumes massive CPU resources to process the stream and causes a sudden escalation in memory allocation. This resource spike degrades application performance, stalls asynchronous event loops, and typically triggers an out-of-memory (OOM) termination of the client process.
The impact of CVE-2026-59881 is classified as a remote denial of service (DoS) and resource exhaustion vulnerability affecting systems that deploy the aiohttp client. This vulnerability does not lead to remote code execution, privilege escalation, or unauthorized access to sensitive application data.
Because the defect is situated within the client-side implementation, the vulnerability is only triggered when the client actively establishes a connection to a hostile endpoint. This limits the threat surface primarily to applications that interact with external, dynamic, or user-provided WebSocket targets, such as crawler bots, feeds, or messaging clients.
The CVSS v4.0 score of 6.9 reflects medium severity, acknowledging that while the impact on system availability is low (restricted to the client application process), the attack complexity is low and requires no user interaction. There is currently no evidence of active exploitation in the wild, and the known exploit is limited to a proof-of-concept functional test within the project's repository.
The standard remediation is upgrading the aiohttp library to version 3.14.2 or higher. This update resolves the unsafe initialization defaults and implements strict RFC 6455 validation for the RSV1 bit. You can perform the upgrade through Python's package manager:
pip install -U "aiohttp>=3.14.2"If upgrading is not immediately possible, application developers should implement defensive controls to limit exposure. Restrict outbound WebSocket connections to validated, trusted domains. Avoid connecting to unencrypted ws:// schemes where data modification by network intermediaries is possible.
Additionally, deploy container-level resource limits (such as cgroup memory limits) to restrict the physical resources available to the application process. This ensures that if a client is targeted by a decompression bomb, the resulting resource consumption is contained and cannot exhaust the entire host system's memory or CPU allocation.
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
aiohttp aio-libs | < 3.14.2 | 3.14.2 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-20 |
| Attack Vector | Network |
| CVSS v4.0 Score | 6.9 (Medium) |
| EPSS Score | 0.00302 |
| Impact | Denial of Service (DoS) / Resource Exhaustion |
| Exploit Status | Proof-of-Concept |
| CISA KEV Status | Not Listed |
The product does not validate or incorrectly validates input that can affect the control flow or data flow of a program.
A high-severity type-confusion vulnerability exists in NearForm fast-jwt prior to version 6.3.0. The vulnerability allows attackers to bypass crucial claim validation steps (such as expiration, issuer, audience, and subject validations) by presenting a validly signed JSON Web Token structured as a JSON array instead of a JSON object. This occurs because the library's decoder fails to reject JSON arrays during type evaluation.
NearForm fast-jwt prior to version 6.3.0 is vulnerable to an input validation flaw where configuring verifier properties (such as clockTolerance, clockTimestamp, and cacheTTL) with non-finite values like Infinity or NaN allows attackers to bypass temporal claim validations, including expiration (exp) and activation (nbf) boundaries. This validation bypass can result in unauthorized session persistence and cache poisoning.
A critical cryptographic vulnerability in fast-jwt versions 6.2.x prior to 6.3.0 allows unauthenticated remote attackers to execute an asymmetric-to-symmetric algorithm confusion attack due to incomplete validation of leading non-whitespace prefixes.
CVE-2026-107720 is a critical signature verification bypass vulnerability in NearForm's fast-jwt Node.js library. Under specific configurations where the token verifier is initialized with a falsy cryptographic key (such as an empty string or null) and a non-empty algorithms allowlist, the library erroneously skips signature validation. This allows unauthenticated remote attackers to submit fabricated, unsigned JSON Web Tokens and bypass the authorization boundary of the application entirely.
Ruby Mechanize prior to version 2.14.1 contains an information disclosure vulnerability. When executing cross-origin HTTP redirects, global headers configured on the Mechanize agent (such as Authorization or Session Cookies) are dynamically re-applied to the subsequent request, bypassing the internal header-stripping logic. An attacker who controls a redirection endpoint can capture sensitive bearer tokens or cookies.
An origin trust boundary failure in the Ruby mechanize library (prior to v2.14.1) allows unauthenticated remote web servers to harvest sensitive global request headers, such as Authorization Bearer tokens and cookies, by utilizing HTML-level meta-refresh redirection tags. Standard HTTP-level redirect boundaries were not applied to document-level redirects, creating a vector for cross-origin credential leakage during automated crawls.