Apr 7, 2026·6 min read·1 visit
Unbounded WebSocket subscriptions in Strawberry GraphQL allow unauthenticated remote attackers to exhaust server memory and CPU, leading to Denial of Service. Fixed in version 0.312.3.
Strawberry GraphQL prior to version 0.312.3 is vulnerable to an unauthenticated Denial of Service (DoS) attack due to unbounded resource allocation in its WebSocket subscription handlers. An attacker can exhaust server memory and CPU by sending a flood of subscription requests over a single connection.
Strawberry GraphQL is a Python library used to construct GraphQL APIs. It provides built-in support for real-time data updates via WebSocket subscriptions, implementing both the modern graphql-transport-ws and legacy graphql-ws subprotocols. These subscription handlers reside primarily in the strawberry.subscriptions.protocols module.
The vulnerability is classified as CWE-770 (Allocation of Resources Without Limits or Throttling). Prior to version 0.312.3, the library failed to enforce any upper bound on the number of concurrent subscriptions a client could initialize over a single established WebSocket connection. This omission directly exposed the underlying asynchronous task scheduler to malicious saturation.
An unauthenticated attacker can exploit this architectural flaw to cause a localized Denial of Service (DoS). By multiplexing an unbounded number of subscription requests through a single socket, the attacker forces the server to allocate unbounded memory objects and CPU cycles. This degradation impacts the processing of all other concurrent requests handled by the Python application.
The root cause of CVE-2026-35526 lies in the unconditional task allocation logic within the WebSocket subscription handlers. When a client transmits a subscribe message under the modern protocol, or a start message under the legacy protocol, the server processes the payload to initiate the requested GraphQL operation.
For every incoming subscription request, the application invokes the asyncio event loop to create a new asyncio.Task. This task manages the lifecycle of the subscription, maintaining an active asynchronous generator that yields results back to the client. The handler executes this allocation step without validating the current operational state or applying a per-connection threshold.
Because an asyncio.Task and its associated generator context require memory to track execution state, local variables, and callback structures, linear memory allocation occurs. Generating thousands of these objects rapidly consumes available RAM. Furthermore, the Python event loop incurs significant scheduling overhead when managing thousands of active tasks, leading to CPU saturation and increased latency for legitimate connection handling.
The system lacks a bounded resource management mechanism at the protocol layer. Without a mechanism to reject excessive concurrent subscription identifiers from a single remote endpoint, the server process reliably encounters an Out-Of-Memory (OOM) condition or becomes entirely unresponsive.
The vulnerable implementation resided within the handle_subscribe and handle_start methods. The code accepted the client-provided id parameter and immediately proceeded to instantiate the execution pipeline, completely omitting any state checks against the connection's active task dictionary.
The patch introduced in commit 0977a4e6b41b7cfe3e9d8ba84a43458a2b0c54c2 remediates this by introducing a configurable threshold, max_subscriptions_per_connection, which defaults to 100. The code was modified to evaluate the length of the active subscriptions dictionary before proceeding with task creation.
# Patched logic in Strawberry GraphQL 0.312.3
if self.max_subscriptions_per_connection is not None:
if len(self.subscriptions) >= self.max_subscriptions_per_connection:
# Connection limit exceeded
await self.send_message({
"type": "error",
"id": operation_id,
"payload": {"message": "Subscription limit reached"}
})
return
# Safe to allocate new subscription task
self.subscriptions[operation_id] = asyncio.create_task(
self.run_subscription(operation_id, payload)
)The fix is comprehensive for the core resource exhaustion vector. The patch correctly intercepts the execution flow before allocating the asyncio.Task, responding with a standard GraphQL error payload. Additionally, the patch fortifies the legacy graphql-ws protocol handler by enforcing that a connection_init phase completes before any start frames are processed.
Exploitation requires minimal prerequisites. The attacker must possess network reachability to the target GraphQL endpoint and the ability to negotiate a WebSocket upgrade. No authentication is necessary unless the GraphQL endpoint implements connection-layer authentication independently of the Strawberry GraphQL defaults.
The attack sequence begins with a standard HTTP GET request containing the Upgrade: websocket headers. Once the TCP socket transitions to the WebSocket protocol, the attacker sends a valid connection_init frame to satisfy protocol handshake requirements. The server responds with a connection_ack frame, verifying readiness to process operations.
The attacker then enters the flood phase. They construct a script to continuously transmit subscribe frames (or start frames for legacy endpoints). Each frame specifies a unique string identifier in the id field. The attacker pipelines these frames continuously without waiting for corresponding responses.
As the server attempts to allocate memory for each new task, internal metrics will reflect severe memory consumption spikes. Eventual process termination is executed by the operating system's OOM killer, finalizing the Denial of Service.
The exploitation of CVE-2026-35526 results in a high-impact Denial of Service condition. The CVSS 3.1 base score is calculated at 7.5 (High), reflecting the ease of network exploitation and the complete loss of availability. Confidentiality and integrity are not compromised, as the vulnerability does not provide arbitrary memory read/write capabilities or unauthorized access to backend data.
The primary consequence is total service disruption for the affected Python application instance. When the GraphQL API runs in an asynchronous framework (such as FastAPI or Starlette), the event loop saturation caused by the excessive tasks blocks the execution of non-GraphQL HTTP endpoints served by the same process.
In containerized environments, the localized resource exhaustion triggers the orchestration platform (e.g., Kubernetes) to terminate the Pod due to memory limit violations. While orchestration mechanisms will automatically restart the container, sustained exploitation causes a persistent crash loop, completely denying service to legitimate consumers.
The primary remediation strategy is upgrading the strawberry-graphql package to version 0.312.3 or later. This release enforces the max_subscriptions_per_connection limit and hardens the legacy protocol initialization flow. Operators must integrate this update into their dependency management pipelines and redeploy affected services.
Administrators must carefully configure the max_subscriptions_per_connection parameter if the application architecture demands a high volume of concurrent real-time feeds per client. The default value is 100. Lowering this value (e.g., to 20 or 50) is recommended for APIs that only require a small number of active subscriptions per user session.
If immediate patching is technically infeasible, operators can mitigate the attack surface at the infrastructure layer. Web Application Firewalls (WAF) or ingress controllers can be configured to enforce aggressive rate limiting on the number of WebSocket frames permitted per connection per second. While this does not solve the underlying state retention issue, it limits the speed at which an attacker can consume server memory.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
strawberry-graphql Strawberry | < 0.312.3 | 0.312.3 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-770 |
| Attack Vector | Network |
| CVSS Score | 7.5 (High) |
| Impact | Denial of Service |
| Exploit Status | PoC |
| CISA KEV | False |
The software does not properly limit the number of subscriptions a single client can initiate, leading to exhaustion of system resources.