GraphQL attacks
TL;DR: Introspect the schema, then abuse aliasing, batching, depth, and field-level auth gaps to enumerate, brute, and DoS the API.
What it is
GraphQL exposes a single endpoint (usually /graphql) that takes structured queries. Compared to REST it concentrates authorisation decisions to per-field resolvers — and shifts complexity from URL routing to query shape. Bugs cluster around schema exposure, missing per-field auth, the alias/batch mechanisms that let one HTTP request fan out into many logical operations, and resource exhaustion via deeply nested queries.
Preconditions / where it applies
- A GraphQL endpoint. Common paths:
/graphql,/api/graphql,/v1/graphql,/query, plus/graphiql//playgroundUIs. - Apollo, Hasura, Graphene, graphql-java, AWS AppSync, or a custom resolver layer.
- Either anonymous access or a low-priv user account.
Technique
- Detect. POST
{"query":"{__typename}"}— a JSON response with"__typename":"Query"is conclusive. Look forerrors[].extensions.codestyle metadata. -
Introspect. Even when explicit introspection is disabled, field suggestions (“Did you mean
userById?”) leak schema. Tools:graphql-cop,InQL,clairvoyance(suggestion-based schema recovery).1
query { __schema { types { name fields { name args { name type { name } } } } } }
- Authorisation per field. Authentication often gates the endpoint but not each resolver. Walk every Query and Mutation field with a low-priv token and see what returns data.
- IDOR via arguments.
user(id: 42),order(id: "..."). Iterate ids exactly like idor over REST. -
Alias batching for brute force. One HTTP request, many logical operations — sails past per-request rate limits.
1 2 3 4 5
mutation { a: login(user:"alice", pass:"p1") { token } b: login(user:"alice", pass:"p2") { token } c: login(user:"alice", pass:"p3") { token } }
- Query batching. Some servers accept arrays of operations:
[{query:"..."},{query:"..."}]. Same effect as aliasing. - Depth / complexity DoS. Self-referential types (
user { posts { author { posts { author { ... } } } } }) generate exponential resolver work. Pair with batching for amplification. - CSRF via GET / form-encoded. Some servers accept
query=viaapplication/x-www-form-urlencoded, opening csrf against mutations because there is no JSON content-type preflight. - Field name injection / GraphQL-over-WebSocket. Subscriptions may expose data not reachable via Query.
Detection and defence
- Disable introspection in production; disable suggestions when the framework allows it.
- Enforce authorisation in every resolver. Use a directive (
@auth) or middleware that fails closed. - Cap query depth, complexity, and aliases per request. Reject query batching if you don’t use it.
- Require
Content-Type: application/jsonand reject GET for mutations; CORS lock the endpoint. - Detection: alerts on
__schema/__typequeries; spikes in alias counts; clients that submit unusually deep queries.
References
- HackTricks — GraphQL pentesting — checklist.
- PortSwigger — GraphQL API vulnerabilities — labs.
- OWASP — GraphQL Cheat Sheet — defensive patterns.