Executive Summary
Imperva Threat Research identified a series of remote denial‑of‑service vulnerabilities in GraphQL Java, one of the most widely used libraries for GraphQL in the Java ecosystem.
In this blog, we’ll deep dive into these vulnerabilities, analyze their impact, and provide recommendations to protect your environment. If your endpoints are protected by Imperva Web Application Firewall, your systems are already protected against these threats.
Introduction
GraphQL Java is the engine beneath Spring for GraphQL, Netflix DGS, and Atlassian’s own products. With over one million downloads per month and ten years of production use, it is a natural choice for any team building a GraphQL API on the JVM.
Over the last few years, multiple types of DoS attacks have been identified across GraphQL implementations. These vulnerabilities can lead to either denial of service or, in cloud-billed environments, excessive cost consumption. In response, the industry converged on a set of protective controls, like depth limiting, query complexity budgets, and field count caps. Apollo added query cost analysis and depth limiting, GitHub meters its public GraphQL API by computed node cost, and GraphQL Java also introduced additional controls.
However, these enhancements can leave blind spots earlier in the pipeline, before the server has even understood the request.
The DoS attack we found live in the first half of the pipeline, where the query is parsed and validated, and therefore independent from the schema, the resolvers, and any backend logic.
They were reported under GHSA-7p4r-9rcc-8vhv (still as draft) and fixed in versions 24.4, 25.1, and 26.1. Several products embedding the library were affected, including Adobe Experience Manager (AEM), Atlassian Confluence, and HAPI FHIR server, a widely used open-source implementation of the HL7 FHIR healthcare interoperability standard.
Request Processing

Fig. 1: GraphQL Processing High Level Pipeline
GraphQL was built around a simple idea: “GraphQL queries mirror their response.” In other words, the client describes exactly the data it needs, while the server determines how to retrieve and resolve that data. The query is then sent to a GraphQL endpoint, where it goes through a series of processing steps before the response is returned:
- HTTP Request: The server receives the GraphQL query.
- Lexing: The query is broken down into tokens.
- Parsing: The tokens are turned into an Abstract Syntax Tree (AST) representing the query structure.
- Validation: The query is checked against the GraphQL schema and its rules.
- Execution: GraphQL invokes the appropriate resolvers to retrieve the requested data.
- JSON Response: The results are assembled and returned to the client.
The distinction between these stages matters. A request can be rejected before execution because its syntax is invalid or because it does not match the schema. Only after passing these checks does GraphQL reach the resolvers, where the application and its underlying data sources are accessed.
For security research, this pipeline provides a useful way to understand the attack surface. Each stage processes attacker-controlled input in a different way, making it important to understand not only what GraphQL does, but also when and where it does it.
Parsing and Validation: A Closer Look
Before a server decides what data to return, it must first decide whether the request makes sense. Parsing breaks the raw query text into an Abstract Syntax Tree. This involves:
- Resolving string literals: block strings go through a dedicated normalization routine that strips leading and trailing blank lines.
- Constructing AST nodes for every value encountered: numeric literals are converted to BigInteger or BigDecimal at this point.
Validation walks the AST and checks it against the schema. This involves:
- Field and type checks: do the requested fields exist? Do the argument types match?
- Fragment validation: named fragments must not form cycles, directly or transitively
- Limit enforcement: in GraphQL Java 26.0, maxDepth and maxFieldsCount are checked here.
Fragment Cycle Detection
Validation is where fragment cycle detection takes place. Named fragments are GraphQL’s reuse mechanism. Instead of repeating the same selection set in multiple places, a client defines it once and references it with a spread:

Fig. 2: Named Fragments in GraphQL
Fragments can themselves spread other fragments. The spec allows arbitrary nesting, but it forbids cycles: if A spreads B and B spreads A, resolving either one requires resolving the other first. The execution engine would recurse indefinitely. The same applies to indirect cycles across any number of fragments.
This check happens during validation, after parsing has produced the AST and before execution starts. At this point the server has a complete map of every named fragment in the document and every spread reference between them.
Cycle detection is pretty straightforward: for each fragment, follow its spread references transitively and check whether any path leads back to the starting node. If it does, the document is rejected with a validation error and execution never starts.
ValidateNoFragmentCycles is called once per FragmentDefinition in the document. Each call starts with a fresh HashMap.
For each fragment, it calls buildTransitiveSpreads, a depth-first traversal that follows every spread reference outward, building up the set of fragments reachable from the starting node.
The traversal tracks its current path in an ArrayList, and at each step calls ArrayList.contains to check whether the next node has already been visited.
Worst Case Scenario
We considered a document with the following chain of d fragments:

Fig. 3: Malicious Payload Structure
For each fragment, ValidateNoFragmentCycles starts a new traversal with a fresh HashMap. Since the traversal follows the entire chain, and ArrayList.contains iterates over the path of fragments visited so far, the cost of a single validation is O(d²).

Fig. 4: Processing of the First Fragment
Since this happens for all fragments, the overall complexity is O(d³), where d is the number of fragments in the document. Within the default 15,000-token limit, a single payload can contain up to around 1,800 fragments (~50 KB), which is sufficient to saturate a CPU core for several minutes at every request.
Why existing GraphQL protections may not help
Traditional GraphQL protections such as depth limits and field-count limits operate during validation. However, this attack specifically targets the validation step itself. Therefore, it hits before the protections can prevent the attack.
Following the reception of our report, graphql-java released a patch that introduces memoization. Once a fragment has been explored, it is marked as done and never re-traversed again. The same malicious payload is processed in a few milliseconds instead.
This finding was the most impactful, but it wasn’t the only one.
Additional Remote DoS Vulnerabilities
We identified the same underlying idea as one of the attack vectors previously identified in React Server Components (BigInt values, CVE-2026-23864), and another vector involving block string normalization routine, more impactful. Both fixed in 24.4, 25.1 and 26.1.
HAPI FHIR Use Case
HAPI FHIR is a Java server designed to store and serve medical data (patients, prescriptions, lab results) using the FHIR healthcare standard. Like many modern APIs, it exposes a GraphQL endpoint that clients can use to query this data.
Internally, HAPI handles GraphQL requests with its own parser, written specifically for FHIR, which knows how to resolve medical resources. But when a client asks for introspection (a standard GraphQL feature that returns the full list of available queries and types) HAPI delegates directly to graphql-java.
However, it’s possible to bypass HAPI’s custom FHIR resolver and route a non-introspection query straight to graphql-java.
This delegation is triggered by a simple string check: it relies on the presence of the string `__schema` in the body.
Therefore, simply starting the request with the following pattern (see Fig 5.) was enough.

Fig. 5: bypass HAPI’s custom FHIR GraphQL resolver
Tested on a fresh c7i.2xlarge instance, with the latest HAPI server with graphql enabled, a single request took 150 seconds of validation. A single wave of multiple small requests could lock the server entirely for several minutes and pin all 8 CPU cores at 100% for more than an hour.
In this application, this attack vector could represent an alternative avenue for a threat actor lacking authorization to exfiltrate patient data (as HAPI’s authorization model governs GraphQL endpoint access and data read permissions independently), and still willing to cause impact.
The attack surface is further widened by HAPI’s default CORS configuration (allows any origin, Access-Control-Allow-Origin: *). In environments where no additional network controls are in place, this means the attack could be triggered from a browser: a healthcare worker opening a malicious link while connected to the internal network is sufficient to take down the FHIR server for everyone.
Who is Affected?
An application is potentially exposed when:
- It uses an affected version of GraphQL Java;
- It accepts attacker-controlled GraphQL documents;
- The GraphQL endpoint is remotely reachable;
- No upstream control blocks the malicious request.
Remediation
The fixes are available in GraphQL Java versions 24.4, 25.1, and 26.1 via PRs #4440, #4441.
For environments where an immediate upgrade is not possible:
- Restrict access to affected GraphQL endpoints to authenticated or otherwise trusted clients where the application permits it.
- Apply rate limiting and concurrency limits to restrict the number of GraphQL requests that can be processed simultaneously.
- Monitor CPU utilization and GraphQL request latency for unusual increases that may indicate resource-exhaustion attempts.
If your endpoints are protected by Imperva Web Application Firewall, your systems are already protected against these threats.
Conclusion
As AI accelerates vulnerability discovery and exploitation, threat actors can increasingly scan a target environment in real time, identify weaknesses across its technology stack, and rapidly turn those findings into attacks. This reduces the time organizations must detect and remediate newly exposed vulnerabilities, making proactive and continuously enforced security controls increasingly important.
Affected organizations should upgrade to a fixed release as soon as possible. Until patches can be applied, additional controls at the application and network layers can help reduce exposure and limit the impact of potential attacks.
Imperva customers are protected against exploitation of these vulnerabilities. Imperva Threat Research will continue to monitor this threat and work to ensure that customers remain protected against emerging attack techniques targeting GraphQL and other widely deployed technologies.
Timeline
August 11 – We reported the security issue to GraphQL Java.
August 19 – Report Acknowledged.
August 23 – Fix released in versions 24.4, 25.1 and 26.1.
Try Imperva for Free
Protect your business for 30 days on Imperva.

