June 3, 2026
GraphQL Introspection: The Feature That Hands Attackers Your API Blueprint
TL;DR: GraphQL introspection is a built-in, spec-compliant feature that — when left enabled on production endpoints — gives attackers a…
johnnattakit 0xDD
5 min read
TL;DR: GraphQL introspection is a built-in, spec-compliant feature that — when left enabled on production endpoints — gives attackers a complete map of your entire API in a single query, dramatically compressing the reconnaissance phase of an attack.
Background
GraphQL was designed with developer experience in mind. One of its most convenient features is introspection: a built-in mechanism that lets any client query the API itself to discover what types, fields, queries, mutations, and subscriptions are available. This powers tools like GraphiQL and Altair, and makes onboarding new developers significantly easier.
The problem is that this same feature works just as well for attackers as it does for developers.
Imperva's research monitoring roughly 6,000 GraphQL endpoints found that approximately 50% had been actively targeted with introspection queries. It is one of the most common GraphQL reconnaissance techniques observed in production traffic — and it requires no credentials, no brute-forcing, and no special tooling beyond a simple HTTP request.
OWASP is explicit about this in the GraphQL Cheat Sheet: "Disable introspection queries system-wide in any production or publicly accessible environments."
Technical Analysis
What Introspection Actually Returns
Introspection operates through reserved fields prefixed with __ (double underscore). The entry point is __schema, available on every root query type by default.
A minimal probe to confirm introspection is enabled:
{ __schema { queryType { name } } }{ __schema { queryType { name } } }That single line is enough to confirm exposure. The full schema dump that most tooling runs automatically:
query IntrospectionQuery {
__schema {
queryType { name }
mutationType { name }
subscriptionType { name }
types {
kind
name
fields(includeDeprecated: true) {
name
args { name type { kind name ofType { kind name } } }
type { kind name ofType { kind name } }
isDeprecated
deprecationReason
description
}
}
directives { name locations args { name type { kind name } } }
}
}query IntrospectionQuery {
__schema {
queryType { name }
mutationType { name }
subscriptionType { name }
types {
kind
name
fields(includeDeprecated: true) {
name
args { name type { kind name ofType { kind name } } }
type { kind name ofType { kind name } }
isDeprecated
deprecationReason
description
}
}
directives { name locations args { name type { kind name } } }
}
}One request. Complete schema graph. Including deprecated fields — which are frequently unpatched legacy code.
The Attack Surface Exposed
Data Exposed Attack Value All query/mutation/subscription names Complete attack surface map Field names and types Targeted field access attempts Argument names and types Injection point identification Descriptions and deprecation notes Business logic clues, developer comments Deprecated fields Legacy code with weaker security controls Type relationships IDOR chain discovery
The 54% figure from AFINE's pentesting research is worth noting here: roughly 54% of known GraphQL vulnerabilities relate to access control. That is not a coincidence. Introspection is what reveals where to look. Once an attacker has the full schema, they know which mutations to test for missing authorization checks, which fields hold PII or financial data, and which deprecated endpoints were never properly decommissioned.
The Attack Kill Chain
- Locate the endpoint — Common paths:
/graphql,/api/graphql,/v1/graphql,/query - Probe introspection — Send
{__schema{queryType{name}}}via GET or POST - Full schema dump — Run the complete introspection query; visualize with Altair or GraphQL Voyager
- Target identification — Look for admin mutations, PII fields, financial data, auth-related resolvers
- Exploit — Chain to IDOR, missing auth checks, injection, batch/alias abuse
Real-World Incidents
These cases illustrate what schema exposure enables in practice:
- GitLab (reported ~2018, illustrative) — An exposed GraphQL schema reportedly revealed internal fields not intended for public access. Specific field names cited in secondary sources are unverifiable against primary advisories; this is presented as an illustrative example of the exposure class, not a confirmed disclosure with named fields. [Single secondary source: TRaViS ASM — no corroborating CVE or vendor advisory]
- Shopify (reported ~2020, illustrative) — Introspection on a Shopify endpoint reportedly exposed fields relating to financial and customer data. As with the GitLab case, specific field names from secondary sources cannot be corroborated against a primary Shopify advisory or HackerOne report. [Single secondary source: TRaViS ASM — no corroborating CVE or vendor advisory]
- Parse Server (CVE-2025–53364) — Unauthenticated access to GraphQL schema without a session token or master key, confirmed and documented by Escape.tech.
Bypassing Disabled Introspection
This is where the common advice falls short. Disabling introspection is a necessary baseline, not a complete solution. Several techniques can partially or fully work around a naive disable:
1. Regex bypass via whitespace/newlines
Developers often block __schema with a regex filter. GraphQL ignores whitespace, but unsophisticated regex does not:
{ __schema
{ queryType { name } } }{ __schema
{ queryType { name } } }Or using a comma: {__schema,{queryType{name}}}
2. HTTP method switching
Defenses may block POST-based introspection but leave GET-based queries (or application/x-www-form-urlencoded) unprotected — or the reverse. Testing both is standard practice.
3. Suggestion-based schema reconstruction (Clairvoyance)
This one is more significant. When a client queries an invalid field, Apollo and many GraphQL servers return helpful error messages:
Cannot query field 'ussr' on type 'Query'. Did you mean 'user'?Cannot query field 'ussr' on type 'Query'. Did you mean 'user'?Clairvoyance automates this: it fuzzes with wordlists and iteratively reconstructs the schema from "Did you mean?" responses alone. Introspection can be completely disabled and an attacker can still recover most of the schema. Disabling introspection without also disabling field suggestions provides a false sense of security.
4. Frontend traffic analysis
Single-page applications and mobile apps make GraphQL queries that are visible in browser dev tools or proxy traffic. Field names and types leak directly from the client without touching the server's introspection endpoint.
5. Staging and dev environment leakage
Introspection is almost universally enabled in staging and development. These environments typically share the same schema as production. Accessing a staging environment — which often has weaker access controls — yields the same schema.
Post-Discovery Exploitation Paths
Once an attacker has the schema, the options include:
- Batch and alias attacks — Use discovered mutations with aliases to bypass rate limiting. A 4-digit PIN brute-force that would normally require 10,000 HTTP requests can be collapsed into a single GraphQL query with 10,000 aliased mutations.
- IDOR via object IDs — Discovered types reveal ID-based resource access patterns that can be iterated.
- Authorization bypass — Mutations not documented in public-facing docs frequently lack access controls entirely.
- Business logic targeting — Fields named
storeCredit,discountCode,adminRole, orinternalNotesare high-value targets that would be invisible without schema discovery.
Proof of Concept (Authorized Lab Environment Only)
The following demonstrates introspection enumeration using InQL in Burp Suite against a local test GraphQL API. Only perform this in an authorized environment.
Step 1 — Confirm introspection is active:
Send to Burp Repeater, POST to /graphql:
POST /graphql HTTP/1.1
Host: target.lab
Content-Type: application/json
{"query":"{ __schema { queryType { name } } }"}POST /graphql HTTP/1.1
Host: target.lab
Content-Type: application/json
{"query":"{ __schema { queryType { name } } }"}A response containing {"data":{"__schema":{"queryType":{"name":"Query"}}}} confirms introspection is enabled.
Step 2 — Full dump with InQL:
In Burp Suite with InQL installed, right-click the request → Send to InQL Scanner. InQL will auto-run the full introspection query and present a browsable schema tree, including all queries, mutations, and types.
Step 3 — Identify targets:
Filter the schema for fields containing keywords: admin, internal, password, token, credit, payment, secret, private. These are immediate candidates for authorization testing.
Step 4 — Test Clairvoyance against a disabled endpoint:
# Install: pip install clairvoyance
clairvoyance https://target.lab/graphql -w /usr/share/wordlists/graphql-fields.txt -o schema_recovered.json# Install: pip install clairvoyance
clairvoyance https://target.lab/graphql -w /usr/share/wordlists/graphql-fields.txt -o schema_recovered.jsonReview schema_recovered.json — even with introspection disabled, significant schema coverage is typically recovered.
Mitigation
Mitigation should be layered, not a single toggle. Ranked by effectiveness:
Control Effectiveness Notes Disable introspection in production High Required baseline; insufficient alone Disable field suggestions (hideSchemaDetailsFromClientErrors) High Eliminates Clairvoyance-style reconstruction Restrict introspection to authenticated/admin roles High Better than full disable for internal APIs where devs need it Field-level authorization on all resolvers Critical Addresses the root cause regardless of introspection status WAF rules for __schema queries Medium Bypassable with special characters; useful as defense-in-depth only Rate limiting on all GraphQL endpoints Medium Slows fuzzing and suggestion-based reconstruction Audit schema for sensitive descriptions Medium Remove internal developer comments and PII field names from descriptions
The most important point: schema-level defenses (disabling introspection, blocking suggestions) reduce reconnaissance efficiency — they do not prevent exploitation if authorization is absent at the resolver level. A determined attacker with frontend traffic access or a staging environment gets the schema anyway. The actual fix is enforcing authorization checks on every resolver, every time.
Framework-Specific Configuration
Apollo Server (Node.js):
const server = new ApolloServer({
typeDefs,
resolvers,
introspection: process.env.NODE_ENV !== 'production',
// Disable suggestions (Apollo Server 4.11.0+)
hideSchemaDetailsFromClientErrors: true,
});const server = new ApolloServer({
typeDefs,
resolvers,
introspection: process.env.NODE_ENV !== 'production',
// Disable suggestions (Apollo Server 4.11.0+)
hideSchemaDetailsFromClientErrors: true,
});GraphQL Yoga:
createYoga({
schema,
graphiql: false,
maskedErrors: true, // masks error contents (execution/parse/validation errors) — does NOT suppress "Did you mean?" field suggestions
// Note: disabling field suggestions requires a separate plugin, e.g. the blockFieldSuggestions envelop plugin (@escape.tech/graphql-armor-block-field-suggestions)
});createYoga({
schema,
graphiql: false,
maskedErrors: true, // masks error contents (execution/parse/validation errors) — does NOT suppress "Did you mean?" field suggestions
// Note: disabling field suggestions requires a separate plugin, e.g. the blockFieldSuggestions envelop plugin (@escape.tech/graphql-armor-block-field-suggestions)
});Hot Chocolate (.NET):
services.AddGraphQLServer()
.AllowIntrospection(false);services.AddGraphQLServer()
.AllowIntrospection(false);References
- PortSwigger Web Security Academy — GraphQL API Vulnerabilities
- OWASP GraphQL Cheat Sheet
- OWASP Web Security Testing Guide v4.2 — GraphQL Testing
- Escape.tech — GraphQL Introspection Security: Risks & Best Practices (CVE-2025–53364)
- Imperva — GraphQL Vulnerabilities and Common Attacks Seen in the Wild
- AFINE — GraphQL Security from a Pentester's Perspective
- Assetnote — Exploiting GraphQL
- TRaViS ASM — The Hidden Dangers of GraphQL Introspection
- HackerOne — How a GraphQL Bug Resulted in Authentication Bypass
- HackTricks — GraphQL Pentesting
- PayloadsAllTheThings — GraphQL Injection
- Intigriti — Five Easy Ways to Hack GraphQL Targets