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.