This chapter follows Pitfalls when updating a GraphQL backend.
REST spreads attack surface across many URLs; GraphQL concentrates it. One POST can request thousands of fields. Security belongs at transport, gateway, and resolver layers—not only in front of the server.
Single endpoint threat model
Attackers know where to probe: POST /graphql. They can:
- Map the API via introspection or field suggestions
- Craft expensive queries (deep trees, wide lists)
- Batch many operations in one HTTP request
- Exploit CSRF if cookies authenticate GET or simple POST
- Trigger verbose errors leaking stack traces or SQL
Treat GraphQL as a privileged interface even when it mirrors internal REST services.
Introspection and suggestions
Introspection exposes the full schema to tools—and scanners. Disable it for anonymous production traffic or require authentication.
Did you mean field hints in validation errors help developers but assist attackers guessing hidden fields. Some gateways redact suggestions in production.
| Control | Goal |
|---|---|
| Disable introspection publicly | Hide schema map |
| Separate admin GraphiQL on VPN | Keep DX for staff |
| Rate limit anonymous requests | Slow enumeration |
Deep and wide queries
Recursive schemas (user { friends { friends { ... }}}) enable depth bombs. Lists with large first arguments cause breadth cost.
Mitigate with depth limits, complexity scoring, timeout, and pagination caps—covered in the next security chapter.
Alias and batch abuse
Multiple aliases can request the same expensive field many times in one HTTP round trip:
query { a: expensive { id } b: expensive { id } c: expensive { id }}Limit aliases, root fields, or total complexity per request.
CSRF and cookies
Browsers send cookies automatically. If your GraphQL endpoint accepts cookie session auth on POST without CSRF tokens, a malicious site can trigger mutations from a victim’s browser.
Prefer Authorization headers for SPAs, or use SameSite cookies plus CSRF tokens. Avoid mutations on GET query strings.
Error leakage and injection
Return generic messages to clients; log details server-side. Map database errors to safe GraphQL errors.
Resolvers that concatenate user input into SQL or NoSQL queries still need parameterized queries—GraphQL does not sanitize resolver code.
Layered controls
flowchart TB edge[WAF and rate limit] gw[Router depth cost APQ] app[NestJS guards per field] data[Parameterized data access] edge --> gw --> app --> data
Check your understanding
Question 1 of 7
0 of 7 checked