How this check works
Scanaris sends a POST request with the smallest possible introspection query, {__schema{queryType{name}}}, to three paths where a GraphQL endpoint usually lives: /graphql, /api/graphql and /v1/graphql. If any of them responds 200 and the body contains __schema or queryType, introspection is enabled and it opens a medium-severity finding with the path. If there is no GraphQL on your site, no path responds that way and there is no finding. The query is read-only, touches no data, and goes through the same time and size limits as the rest of the scan.
Why it matters
Introspection is the GraphQL feature that lets you ask the API about its own schema: every type, every field of every type, every available query and mutation, with their arguments. In development it is essential: it is what the playground, editor autocompletion and code generators rely on. In production, open to anyone, it is a complete map of your backend. An attacker sees that a deleteUser(id) mutation or an internalNotes field exists without having to guess, and goes straight to testing whether the authorisation on each one is done properly. That is why it is medium severity: it exposes no data by itself, but it makes any other permission flaw trivial to find.
How to disable it in production
- Apollo Server:
introspection: "production" !== 'production'when creating the server. Since Apollo 4 it is already off whenNODE_ENVisproduction; if you see it enabled, check that the variable actually reaches your deployment. Also disable the landing page or playground in production. - GraphQL Yoga and Envelop: the
useDisableIntrospection()plugin from@envelop/disable-introspection, conditioned on the environment. - Hasura (the /v1/graphql path): the project's security settings include a per-role introspection control (Cloud and Enterprise). Also make sure the public role has minimal permissions and that the admin secret never travels to the browser.
- graphql-ruby:
disable_introspection_entry_pointsin the schema class. - Python (Graphene, Strawberry, Ariadne): add the
NoSchemaIntrospectionCustomRulerule fromgraphql-coreto the validation rules in production. - .NET (Hot Chocolate):
.AllowIntrospection(false), or the option to toggle introspection per request based on a header or a role.
Disabling introspection is no substitute for authorisation: every query and mutation must check who is asking. Pair it with query depth and complexity limits (they prevent nested queries that take the server down) and, if your server offers it, also turn off field suggestions ("Did you mean…"), which leak names even with introspection closed.
Check it is fixed
curl -s -X POST https://yourdomain.com/graphql -H "Content-Type: application/json" -d '{"query":"{__schema{queryType{name}}}"}'
Repeat with /api/graphql and /v1/graphql. Expected: an error such as "introspection is not allowed", a 400 or 403, or a 404 if there is no GraphQL there. If you see "queryType":{"name":"Query"}, it is still open.
Frequently asked questions
My API is public and documented. Does this affect me?
If you publish the schema on purpose, open introspection reveals nothing new and you can mark the finding as accepted. Most application APIs are not in that situation: they have internal mutations nobody should know about.
My endpoint is on another path. Is it not checked?
Only the three most common paths are probed. If yours lives elsewhere, run the test above against your real path. Other panels and development endpoints that should not respond in production are looked for by exposed endpoints.
What if the endpoint requires authentication?
Then introspection responds 401 and is not flagged. That is the right situation: an attacker without a session gets no schema. Make sure the API's CORS header is not open to any origin; that is what permissive CORS looks at.