Uniform introspection blocking versus conditional access: resolving inconsistent __schema and __type exposure in production GraphQL
0 reputation · 02 Jul 2021, 05:21 UTC
0 reputation · 02 Jul 2021, 05:21 UTC
In production GraphQL deployments, teams must choose between fully disabling introspection to prevent unauthenticated schema discovery and conditionally allowing it for authenticated developer tooling. A specific complication arises because some GraphQL server implementations and middleware treat the __schema and __type meta-fields independently, meaning a policy intended to block introspection may still allow partial schema reconstruction through one field while blocking the other. This inconsistency creates a gap where the API surface is not fully hidden even when introspection is nominally disabled.
The trade-off intensifies because full uniform blocking of both meta-fields breaks generic IDEs, schema registries, and client code generation, while conditional gating introduces authentication complexity and the risk that validation errors or partial responses leak type names and field structures.
Should production introspection policies enforce uniform blocking of both __schema and __type to eliminate partial exposure, or is selective blocking acceptable when paired with other safeguards? What verification steps confirm that a chosen server or middleware consistently applies the policy across both meta-fields? Where does resolver-level authorization fit if introspection controls cannot guarantee complete schema concealment?
Block both __schema and __type uniformly for unauthenticated clients. Selective blocking is not acceptable because __type can reconstruct type names and field structures even when __schema is disabled. Conditional access for authenticated developer tooling is reasonable, but both meta-fields must be gated by the same authorization decision before validation or execution.
The likely explanation is that middleware or server configuration matches the literal field name __schema while leaving __type untouched. Some implementations also treat the two meta-fields as separate switches. In graphql-js, NoSchemaIntrospectionCustomRule is the established validation rule for rejecting introspection types, but custom middleware that filters only __schema can leave __type exposed. Treat this as a probable cause until verified in your deployed version.
For production, enforce a single policy that denies both meta-fields to unauthenticated contexts. For authenticated developer tooling, allow both or deny both—never mix. Apply the gate before validation so that unauthorized introspection requests receive the same generic error as invalid requests.
From an unauthenticated client, send both queries and confirm identical generic errors with no data:
curl -s -X POST https://api.example.com/graphql \
-H 'Content-Type: application/json' \
-d '{"query":"{ __schema { queryType { name } } }"}'curl -s -X POST https://api.example.com/graphql \
-H 'Content-Type: application/json' \
-d '{"query":"{ __type(name: \"KnownType\") { name fields { name } } }"}'Repeat with aliases, fragments, and batched requests to ensure blocking is not based on a single literal string. Run the same checks through your edge, including persisted queries or allowlists, because those can bypass generic middleware if they permit introspection operations.
Query a nonexistent field and confirm production validation errors do not include “Did you mean” suggestions or concrete type names. Error messages that distinguish “introspection disabled” from “not authenticated” confirm policy existence and aid probing.
Introspection controls reduce discoverability; they are not authorization. Enforce field and object authorization in resolvers or the data layer. Use persisted queries or allowlists, depth/complexity limits, and rate limiting as additional safeguards. Note that __typename is a separate meta-field and may still reveal concrete type names for interfaces or unions; blocking __schema and __type does not make type names secret.
One detail changes the recommendation: does your current middleware or server version expose __type when __schema is blocked? If yes, fix the gate immediately. If no, verify the exact deployed version because behavior varies across implementations and releases.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.