A developer's guide to understanding and mitigating the risks of query-based Denial-of-Service attacks in GraphQL APIs.
GraphQL has revolutionized how we build and consume APIs. By allowing clients to request exactly the data they need—no more, no less—it offers unparalleled flexibility and efficiency compared to its RESTful predecessors. But this power is a double-edged sword. That same flexibility, if left unchecked, provides attackers with a powerful arsenal to launch devastating Denial-of-Service (DoS) attacks that can take down entire servers with a single, maliciously crafted query.
As a penetration tester who specializes in API security, I have taken down production GraphQL servers with a simple, five-line query. The frightening reality is that most developers, enamored with GraphQL’s capabilities, are completely unaware of how fragile their APIs are. They deploy without the necessary safeguards, creating a ticking time bomb. This isn’t about massive traffic volume; it’s about weaponizing query complexity.
The core design philosophy of a REST API is that the server dictates the shape and size of the response. A call to /users/{id} returns a predefined user object. The client has no control over how much data or how many database joins occur on the backend. This makes it relatively resilient to query-based resource exhaustion.
GraphQL inverts this model. The client sends a query that describes the data it wants, and the server is obligated to fulfill that request. This is incredibly efficient for legitimate use cases, but it gives attackers a direct lever to control your server’s resource consumption. An attacker is no longer limited to requesting predefined resources; they can craft a query specifically designed to be as computationally expensive as possible. This is the fundamental design choice that makes GraphQL security a unique and critical challenge.
While there are many theoretical attacks, my research shows that three primary vectors are being actively exploited in the wild.
This is the most common and devastating GraphQL DoS attack. If your API schema has circular or recursive relationships, an attacker can exploit them to create an exponentially expensive query.
Consider a simple social media schema where a User has friends, and each friend is also a User:
graphqltype User {
id: ID!
name: String
friends: [User]
}
A malicious actor can craft a query that recursively traverses this relationship, forcing your server to execute an exponentially growing number of database lookups.
The Malicious Query:
graphqlquery maliciousNestedQuery {
user(id: "123") {
friends {
friends {
friends {
# ...repeated 20 times...
friends {
id
name
}
}
}
}
}
}
A nesting level of just 10 or 20 could result in millions or even billions of database operations, instantly consuming all available CPU and memory and crashing the server. This exact vulnerability has been reported in major platforms like GitLab, where a nested query could cause a complete denial of service.gitlab
GraphQL allows clients to use aliases to request the same field multiple times in a single query. While intended for legitimate use cases, it can be abused to force the server to perform redundant work.
The Malicious Query:
graphqlquery maliciousAliasQuery {
user(id: "123") {
field1: complexComputedField
field2: complexComputedField
# ...repeated 500 times...
field500: complexComputedField
}
}
Even if complexComputedField is a simple piece of data, if it’s the result of a computationally expensive resolver function, the server will execute that function 500 times. This can bypass simple depth or complexity limits but still exhaust server resources, making it a subtle but effective API denial of service vector.invicti
GraphQL allows clients to send an array of queries in a single HTTP request to reduce network latency. An attacker can abuse this by sending a batch of 100 individually complex queries at once.
The Malicious Request Body:
json[
{ "query": "{ /* First complex query */ }" },
{ "query": "{ /* Second complex query */ }" },
{ "query": "{ /* Third complex query */ }" }
// ... and 97 more ...
]
This is particularly dangerous because it bypasses traditional IP-based rate limiting. From the network’s perspective, it’s just one HTTP request. But from the server’s perspective, it’s an instruction to execute 100 expensive operations simultaneously. This exact attack vector was identified in MLflow (CVE-2025-0453), where it could be used to cause a denial of service.nvd.nist
Defending against these attacks requires moving security from the network edge into the application layer itself.
This is your first and most basic line of defense. Configure your GraphQL server to parse incoming queries and reject any that exceed a predefined nesting depth.
graphql-depth-limit. javascriptimport depthLimit from 'graphql-depth-limit'; const server = new ApolloServer({ schema, validationRules: [depthLimit(7)] });Depth limiting is a blunt instrument. A more sophisticated approach is to calculate a “complexity score” for each query before execution.
graphql-validation-complexity and graphql-cost-analysis can automate this process.The GraphQL introspection system is a powerful development tool that allows clients to query the schema itself. In a production environment, it is a massive security risk. It provides an attacker with a complete, machine-readable roadmap of your entire API, allowing them to easily find recursive relationships and expensive fields to target.akto
Even a “safe” query can become a DoS vector if its underlying resolver is slow. A hard timeout on every resolver prevents a single long-running query from tying up a server process indefinitely.
For maximum security, you can disallow arbitrary queries in production entirely. With persisted queries, the client sends a pre-registered hash (e.g., SHA-256) of a query instead of the full query string. The server maintains an allow-list of approved query hashes and their corresponding query strings.
GraphQL’s power is its flexibility. Its greatest weakness is also its flexibility. Without the proper safeguards, a modern GraphQL API is a fragile system, vulnerable to a complete takedown by a single, well-crafted request.
The era of relying on a network firewall to protect your application is over. The defense against query-based DoS attacks must live within the application itself. By implementing strict controls on query depth, complexity, and execution time, you can reclaim control over your server’s resources and turn a fragile API into a resilient one. For a comprehensive overview of API security, refer to our API Security Implementation Guide. If an attack does occur, our Incident Response Framework Guide provides the necessary steps to manage the fallout.
This is not a warning about a future threat. This is a debrief of an…
Let's clear the air. The widespread fear that an army of intelligent robots is coming…
Reliance Industries has just announced it will build a colossal 1-gigawatt (GW) AI data centre…
Google has just fired the starting gun on the era of true marketing automation, announcing…
The world of SEO is at a pivotal, make-or-break moment. The comfortable, predictable era of…
Holiday shopping is about to change forever. Forget endless scrolling, comparing prices across a dozen…