The Darkside of GraphQL
This talk demonstrates how GraphQL introspection, query batching, and directive overloading can be exploited to bypass security controls and perform unauthorized actions. It focuses on the inherent risks of misconfigured GraphQL endpoints, specifically regarding authentication bypass and denial-of-service. The speaker provides practical examples of how these techniques can be used to manipulate application data and bypass 2FA mechanisms. The presentation emphasizes the importance of implementing depth limiting, query cost analysis, and rate-limiting to secure GraphQL APIs.
How GraphQL Introspection and Batching Can Lead to Full Account Takeover
TLDR: GraphQL endpoints often expose their entire data model through introspection, which provides a roadmap for attackers to identify sensitive fields and bypass authentication. By combining this with query batching, researchers can brute-force 2FA tokens or trigger denial-of-service conditions by overloading the server with thousands of sub-queries in a single request. Security teams must disable introspection in production and implement strict depth limiting and query cost analysis to prevent these common misconfigurations.
Developers love GraphQL because it solves the over-fetching problem inherent in REST. It gives the client exactly what it asks for, nothing more and nothing less. But from an offensive security perspective, that flexibility is a massive liability. When a backend is configured to allow introspection, it essentially hands an attacker the keys to the kingdom, mapping out every available query, mutation, and data relationship. If you are testing a modern web application, you are likely looking at a GraphQL endpoint that is leaking its entire schema.
The Introspection Trap
Introspection is a feature that allows a client to query the GraphQL schema at runtime. While useful for development, leaving it enabled in production is a critical security misconfiguration. When you hit an endpoint with an introspection query, the server returns a complete map of the data model.
Tools like GraphQL Voyager turn this raw JSON output into a visual graph. For a pentester, this is the reconnaissance phase on steroids. You can see exactly which mutations exist, what arguments they require, and how different nodes are linked. If you see a mutation like updateUserPrice or verify2FAToken, you have found your target. The danger is that developers often assume these internal-facing mutations are protected by obscurity, but the schema makes them public knowledge.
Bypassing 2FA with Query Batching
Once you have the schema, the next step is often exploiting the way the server handles requests. GraphQL supports batching, which allows a client to send an array of queries in a single HTTP request. This is intended to improve performance, but it is a goldmine for broken access control and brute-force attacks.
Consider a standard 2FA verification flow. You submit an email and an OTP. If the server is vulnerable to batching, you can send a single request containing thousands of potential OTPs. If the backend processes these in a loop without rate-limiting, you can brute-force a six-digit code in seconds. The server treats the batch as a single request, often bypassing standard rate-limiting mechanisms that only track the number of HTTP requests rather than the number of operations performed.
You can test for this using BatchQL, a tool designed to identify and exploit batching vulnerabilities. If you see the server returning multiple responses for a single request, you have a high-probability path to an authentication bypass.
mutation {
first: verifyOTP(input: {code: "1234"}) { success }
second: verifyOTP(input: {code: "5678"}) { success }
third: verifyOTP(input: {code: "9012"}) { success }
}
Denial of Service via Directive Overloading
Beyond authentication bypass, GraphQL is uniquely susceptible to resource exhaustion. Directive overloading is a technique where an attacker uses runtime directives like @skip or @include to manipulate the execution path. By nesting these directives or creating complex, circular queries, you can force the server to perform significant computation for a single, small request.
If you send a query that asks for a user, then their friends, then their friends' friends, and so on, you can quickly hit the database hard. If the server does not have a depth limit, this is a trivial way to cause a denial-of-service. The impact is amplified when combined with batching, as you can trigger multiple expensive operations in one go.
Defensive Hardening
Defending against these attacks requires moving away from the "default-allow" mindset. First, disable introspection in your production environment. There is no reason for a client to know your entire schema structure. Second, implement depth limiting. A query that is ten levels deep is almost certainly malicious or poorly written. Third, use query cost analysis. Assign a "cost" to each field in your schema and reject any query that exceeds a predefined threshold.
Finally, treat batching as a potential attack vector. If your application does not strictly require batching, disable it. If you must keep it, ensure your rate-limiting logic is aware of the number of operations within a batch, not just the number of HTTP requests.
The shift to GraphQL has been rapid, but the security tooling and mindset are still catching up. As a researcher, your focus should be on the schema. If you can map it, you can break it. Start by checking for introspection, then look for batching, and finally, test the limits of the server's resource allocation. Most of the time, you will find that the developers have left the front door wide open.
Vulnerability Classes
Tools Used
Attack Techniques
All Tags
Up Next From This Conference
Similar Talks

Kill List: Hacking an Assassination Site on the Dark Web

Unmasking the Snitch Puck: The Creepy IoT Surveillance Tech in the School Bathroom




