ASVS 5.0 V4.3.2 (level 2) requires that GraphQL introspection queries are disabled in production, unless the API is meant to be used by other parties.
Introspection is the built in feature that lets a client ask the server for its whole schema: every type, field, argument and mutation. Developer tools use it to power autocomplete and docs. An attacker uses it to get a free map of your API.
Why it matters
Introspection will not create vulnerabilities by itself. It makes finding them cheap. With the schema in hand, an attacker sees admin mutations, deprecated fields, internal types and odd arguments, and can aim authorization and injection tests at exactly the right places. Without it they have to guess.
Do not mistake this for access control. Hiding the schema does not replace authorization on every resolver. It removes a convenience for attackers and reduces noise, so it is a hardening step, not a fix.
How to implement it
Turn it off by environment. Apollo Server disables introspection by default when NODE_ENV is production, but set it explicitly so a config change cannot flip it silently:
import { ApolloServer } from '@apollo/server';
import { ApolloServerPluginLandingPageDisabled } from '@apollo/server/plugin/disabled';
const isProd = process.env.NODE_ENV === 'production';
const server = new ApolloServer({
typeDefs,
resolvers,
introspection: !isProd,
plugins: isProd ? [ApolloServerPluginLandingPageDisabled()] : [],
});
In other stacks, use the equivalent: a validation rule that rejects __schema and __type fields, such as the NoSchemaIntrospectionCustomRule that ships with graphql-js.
import { NoSchemaIntrospectionCustomRule } from 'graphql';
// pass in validationRules for production
Also turn off field suggestions ("Did you mean...") where your server supports it, because error messages can leak field names even when introspection is off. And remove GraphiQL or any playground from production builds.
How to test it
Send the standard introspection query to production.
curl -s https://api.example.com/graphql \
-H 'content-type: application/json' \
-d '{"query":"{ __schema { types { name } } }"}'
The response must be an error, not a schema. Next, send a misspelled field and read the error.
curl -s https://api.example.com/graphql -H 'content-type: application/json' \
-d '{"query":"{ usr { id } }"}'
If the message says "Did you mean user?" your server still leaks field names. Disable suggestions. Run both checks in staging with production settings, and again after every deploy.
Common mistakes
- Disabling introspection in code but leaving a playground or schema download route in place.
- Forgetting a second GraphQL endpoint, such as an internal or versioned path.
- Leaving field suggestions on, which lets attackers rebuild the schema by guessing.
- Treating hidden schema as protection and skipping resolver level authorization.
- Blocking introspection on an API that is meant for partners, and breaking their tooling.
Quick check
Does turning off introspection secure a GraphQL API?
No. It only makes reconnaissance harder. You still need authorization checks in every resolver.
Sources
- OWASP Application Security Verification Standard, requirement V4.3.2 (level 2)
- GraphQL specification, introspection