GraphQL in Production · Security, Observability and Evolution
Hardening Public GraphQL Endpoints
Introspection, batching, CSRF and the OWASP GraphQL guidance.
Beyond cost limits: disable introspection and field suggestions in production for non-public schemas (they aid reconnaissance), limit batched operations per request, require Content-Type: application/json and non-simple headers to prevent CSRF on cookie-authenticated GET/POST, apply input validation and output encoding, and rate-limit mutations like login per user/IP — including attempts hidden in aliases. Log operation names and hashes, not full variables containing secrets.
GraphQL security checklist
| Risk | Mitigation |
|---|---|
| Schema reconnaissance | Disable introspection/suggestions (private APIs); schema registry for devs |
| DoS via expensive queries | Depth, cost, timeouts, trusted documents |
| Batching/alias brute force | Limit operations per request; rate-limit per operation and alias |
| CSRF (cookie auth) | Require JSON content type/preflight; no mutations via GET |
| Injection in resolvers | Parameterized queries, validation (same as REST) |
| Sensitive data in logs | Log operation name/hash, redact variables |
# GraphQL CSRF: a 'simple' cross-site POST can't set application/json without preflight.
# Reject requests whose Content-Type isn't application/json or application/graphql-response+json,
# and never execute mutations over GET.
POST /graphql HTTP/1.1
Content-Type: text/plain ← reject (simple request, no CORS preflight)
Common pitfalls
- Assuming disabling introspection is sufficient security.
- Mutations allowed over GET.
- Logging full variables with passwords or tokens.
Key points
- Limit, allowlist, and rate-limit
- Block simple-content-type requests for CSRF
- Introspection off for private schemas
Further reading: OWASP GraphQL Cheat Sheet
Practice questions
Why is requiring Content-Type: application/json a CSRF defense for GraphQL?
Answer
Browsers can send cross-site 'simple' requests (form posts with text/plain or form-encoded bodies) without a CORS preflight; JSON content type triggers a preflight that the server's CORS policy blocks.
How can aliases bypass naive login rate limits?
Answer
An attacker can include hundreds of login mutations under different aliases in one request; limits must count operations/fields, not HTTP requests.
Open in the app to mark lessons complete, take notes and track your progress.