GraphQL Introspection in Production: Risks Hunters Actually Abuse
Introspection is a development convenience that sometimes ships to production unchanged. One query can list every type, field, argument, and mutation the schema knows about. That does not equal remote code execution. It does equal a perfect target list.
I've closed more GraphQL reports by proving a mutation authorization miss than by arguing that __schema should be off.
Confirm introspection carefully
Send a minimal probe:
{
__schema {
queryType { name }
mutationType { name }
}
}
If that works anonymously, follow with field names on high-value types. Some gateways block the classic query but still answer __type(name: "User"). Others disable introspection yet leak the same shape through overly helpful error messages when you request nonexistent fields.
Also try authenticated introspection. An admin schema that appears only after login can still reveal operations a support role should never see. GraphQL over GET sometimes slips past a WAF rule that only inspects POST bodies—retry there before declaring the feature dead.
Read the schema like a threat model
Export types related to users, organizations, payments, invites, and uploads. Mutations named updateUser, impersonate, exportData, or setRole deserve first attention. Arguments that take IDs, emails, or URLs are your input surface.
Build small queries instead of dumping everything into the report. Ask for your own object's fields, then change the ID. Try a mutation the UI never exposes to your role. GraphQL authorization bugs love living one field deeper than the REST equivalent—readable ssn on User while the list query looks locked down.
Interface and union types hide fields until you add the right fragment. When introspection shows payment method implementations, query each concrete type. Hunters miss billing emails that only appear on the subtype.
Batching and aliases can amplify a weak rate limit. Use that only on accounts you own and keep volume tiny. The goal is a clear authz failure, not a load test.
Introspection alone versus exploitation
Programs differ. Some accept production introspection on a sensitive API as a low finding. Others want impact. Assume you need impact unless the policy says otherwise.
Strong impact examples:
- Mutation that changes another tenant's settings with your session
- Field that returns secrets belonging to a second owned account
- Nested query that bypasses a REST check the UI relies on
Weak reports paste a 4,000-line schema and stop. Strong reports show the exact GraphQL operation, the role used, and the canary data returned. Even when the program scores introspection itself, attach one abuse path so severity is obvious.
Hardening notes that belong in the writeup
Disable introspection in production or restrict it to break-glass roles. Enforce authorization in resolvers per field when needed, not only at the HTTP gateway. Prefer allowlisted operations for public clients. Log and rate-limit costly nested queries. Persisted queries help mobile apps only if arbitrary queries are rejected outright.
PortSwigger's GraphQL material is a solid shared vocabulary for describing introspection, batching, and CSRF-style cookie issues without inventing a new taxonomy. Cite the operation that mattered. Leave the rest of the schema in your notes.