Is your GraphQL endpoint handing strangers your entire schema through introspection?
GraphQL has a feature that makes the whole ecosystem work: introspection. Ask a GraphQL endpoint the right query and it describes itself, every type, every field, every argument, every mutation, in full. That is how your IDE autocompletes, how GraphiQL renders its docs panel, how client codegen knows your schema. It is genuinely useful, and it is on by default in most setups.
That is also the problem. Introspection does not check who is asking. Left enabled on a production endpoint, the same query that powers your dev tooling hands a stranger the complete, machine-readable blueprint of your API, including the parts you never linked, never documented, and assumed nobody would find.
What an attacker reads off the schema
Normally the slow part of attacking an API is mapping it: guessing field names, working out which arguments each query takes, finding the mutations nobody advertises. Introspection collapses that work to a single request. The response is your entire schema in a clean, structured form an attacker can feed straight into tooling.
The schema names the dangerous mutations explicitly, the impersonate, the deleteUser, the setRole, the internal admin operations that have no business being discoverable by an anonymous caller. It exposes fields a query might over-return, an isAdmin, a passwordHash, a ssn sitting on a type the UI never displays. It documents the exact arguments each operation expects, so an attacker knows precisely what to send. Mapping that would normally take patient probing; introspection prints it.
Turning introspection off is not quite enough
Disabling introspection is the right first move, but many GraphQL servers have a quieter leak that survives it: field suggestions. Send a query with a slightly-wrong field name and the server, trying to be helpful, replies "Did you mean emailAddress?" That single hint confirms a field exists and reveals its real name. Repeat it against a list of guesses and an attacker can reconstruct a meaningful chunk of the schema field by field, even with introspection fully disabled.
So the schema being readable is the finding, and there are two ways it leaks: the introspection query answering, and field suggestions confirming names one at a time. A production GraphQL endpoint should do neither for an anonymous caller.
Disable introspection and suggestions in production
The clean fix is to turn both off in your production configuration, while keeping them on in development where they earn their keep.
// Apollo Server: introspection on by default
const server = new ApolloServer({
schema,
// introspection enabled, suggestions enabled
});Disable introspection in production so the __schema query returns nothing to an anonymous caller, and strip field suggestions from error responses so a wrong field name does not confirm the right one. If your tooling or partners genuinely need the schema in production, gate it behind authentication rather than leaving it open, the same way you would gate a public Swagger UI rather than serve your API docs to the world. The goal is the same: the full map of your API should not be the easiest thing on your domain to read.
This is checkable from outside, because if introspection is on, your own front-end's GraphQL traffic often carries the proof. SurfaceCheckr watches the API responses your page fires during a real-browser render and flags a __schema or __type payload coming back, or a "Did you mean" field suggestion in an error, the two signals that your schema is readable. It reads the responses your page already requested and stays passive: it does not send its own introspection query, replay traffic, or probe field names, because forcing the endpoint to talk is active testing and we don't do that. What it can confirm is whether the blueprint is already in the open. Whether it should be is one config line away.
Find it before someone else does.
Paste your domain. The grade and issue count are free, and you'll see in a couple of minutes exactly what's reachable from outside.