May 5, 2026·6 min read·8 visits
A flaw in webonyx/graphql-php's parser allows attackers to crash the PHP process via highly nested GraphQL queries, bypassing application-level validation. The issue is fixed in version 15.32.3 by implementing a default recursion limit of 256.
An uncontrolled recursion vulnerability (CWE-674) in the webonyx/graphql-php library allows unauthenticated remote attackers to trigger a Denial of Service (DoS). The vulnerability resides in the recursive descent parser, which fails to limit the depth of nested structures, leading to a stack overflow and subsequent PHP process crash.
The webonyx/graphql-php library provides a robust PHP implementation of the GraphQL specification. It processes incoming GraphQL requests, parsing the query strings into an Abstract Syntax Tree (AST) before executing the requested operations against an application's data layer. This parsing phase is fundamental to the library's operation and handles all incoming user input automatically.
A vulnerability exists in the library's parser mechanism, specifically identified as CWE-674: Uncontrolled Recursion. The parser implements a recursive descent methodology to evaluate GraphQL document structures. Prior to version 15.32.3, this implementation lacked boundary limits on the depth of recursion it would permit during the initial lexical analysis.
When presented with a highly nested document structure, the parser enters an unconstrained recursive loop. This behavior exhausts the available call stack memory allocated to the executing process. The ensuing stack overflow results in an immediate denial of service condition by crashing the process handling the request.
The flaw resides within the Language\Parser class of the webonyx/graphql-php package. The parser translates standard GraphQL query strings into a programmatic AST representation. It maps specific structural elements of the GraphQL grammar, such as selection sets and type references, directly to recursive PHP method calls.
Key methods responsible for this recursive processing include parseSelectionSet(), parseValueLiteral(), and parseTypeReference(). Each method calls itself or a companion method to handle nested structures, such as a field requesting an inner object or a multi-dimensional array input. The structural design omitted state variables to track the current depth of this recursive descent.
As the parser descends into deeply nested structures, it continuously allocates new stack frames for each recursive method invocation. Once the number of frames exceeds the operating system's stack size limit for the PHP process, a memory access violation occurs. In PHP environments, this triggers a SIGSEGV (Signal 11), causing the entire process worker to terminate abruptly.
The vulnerability was remediated in commit 7b7f2080ca5f7d5340a696fc5701b19a9222d2c2. The patch addresses the root cause by introducing stateful depth tracking to the Language\Parser class. This mechanism actively monitors the recursion depth during the initial parsing phase and applies upper boundary limits.
The developers added a recursionDepth integer property to track the current depth, alongside a configurable recursionLimit property with a default value of 256. A new validation method, increaseRecursionDepth(), was introduced to increment the counter and enforce the maximum permitted depth. This method is now invoked at the entry points of all recursive loops within the parser architecture.
When the recursion limit is breached, the parser interrupts the execution flow by throwing a GraphQL\Error\SyntaxError. This architectural change converts an uncatchable process-level fault into a catchable application-level exception. The following snippet illustrates the core logic introduced in the fix:
private function increaseRecursionDepth(): void
{
if ($this->recursionLimit > 0 && $this->recursionDepth >= $this->recursionLimit) {
throw new SyntaxError(
$this->lexer->source,
$this->lexer->token->start,
"Recursion depth limit of {$this->recursionLimit} exceeded"
);
}
++$this->recursionDepth;
}Exploitation requires no authentication and relies on sending a single crafted HTTP request to the vulnerable GraphQL endpoint. The attacker must supply a query string containing artificially deep nesting. This nesting can be achieved using nested lists, recursive field selections, or layered type declarations within the payload.
The execution timing of the vulnerability significantly increases its severity. The stack overflow occurs during the lexical analysis and parsing phase. This phase strictly precedes the application of standard GraphQL validation rules, such as maximum query complexity scoring or explicit depth limits defined by the application layer framework.
Proof-of-concept payloads leverage standard GraphQL syntax repeated continuously. For example, deeply nested lists can be triggered via query ($var: [[[[[[[[Int]]]]]]]]) { field }. Similarly, deeply nested selections utilize recursive field structures like { a { a { a { ... } } } }. The parser attempts to resolve these structures entirely in memory, triggering the fault before returning a response.
A successful attack results in a Denial of Service (DoS) affecting the availability of the targeted application. The uncatchable SIGSEGV fault bypasses standard PHP error handling structures, including global exception handlers. This causes the executing PHP-FPM worker process or PHP CLI instance to terminate without returning an HTTP response or executing necessary clean-up routines.
Repeated exploitation systematically terminates available worker processes in the application server pool. If the attack rate exceeds the process manager's ability to spawn new replacement workers, the entire application becomes unresponsive to legitimate user traffic. This creates a high availability impact with minimal resource expenditure or network bandwidth requirements on the attacker's side.
Modern runtime protections mitigate some recursion issues but prove insufficient in this context. The zend.max_allowed_stack_size directive introduced in PHP 8.3 attempts to prevent segmentation faults by throwing an Error exception before the stack guard page is hit. However, in certain environment configurations, the raw depth of the GraphQL parser payload circumvents this check, making the application-level patch strictly necessary.
The primary remediation strategy requires updating the webonyx/graphql-php library to version 15.32.3 or later. This release introduces the required structural constraints to prevent uncontrolled recursion. System administrators and developers should execute composer update webonyx/graphql-php to deploy the fixed package to their environments.
Developers manually instantiating the Parser component must verify they do not inadvertently disable the new depth controls. The recursionLimit parameter defaults to 256, which provides ample margin for all legitimate GraphQL schemas. Explicitly setting this configuration to 0 removes the protection and re-exposes the application to the DoS vector.
In environments where immediate patching is unfeasible, administrators can deploy Web Application Firewall (WAF) rules as an interim mitigating control. These rules should explicitly block incoming payloads containing excessive consecutive structural characters, such as more than 100 sequential open brackets ([) or open braces ({). This heuristic approach successfully intercepts the fundamental structural prerequisite for the attack.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
graphql-php webonyx | < 15.32.3 | 15.32.3 |
| Attribute | Detail |
|---|---|
| Vulnerability Class | CWE-674: Uncontrolled Recursion |
| Attack Vector | Network (Unauthenticated) |
| Impact | High (Denial of Service via Process Crash) |
| Exploit Status | Proof of Concept Available |
| KEV Status | Not Listed |
| Affected Component | Language\Parser class |
The product does not properly control the amount of recursion that takes place, consuming excessive resources, such as allocated memory or the program stack.