GraphQL
GraphQL Introspection and Denial-of-Service Controls
Published: 19 September 2026
Discoverability and query cost are separate concerns, and each needs a deliberate policy and a live test.
Decide who needs schema discovery
GraphQL introspection lets tooling and clients discover available types and fields. For a public API, that may be an intended feature; for an internal graph, anonymous discovery may be unnecessary. Choose a policy based on the client model. If production introspection is restricted, give authorised developers another way to obtain the current schema and keep it in sync with the deployed graph.
Do not count a disabled introspection endpoint as an access-control measure. A client can send a known field name without discovering it first. Every resolver must still check permission for the requested object and property. Test an unauthorised caller with a direct known-field query as well as an introspection query. The expected result should be driven by policy, not by obscurity.
Separate depth, breadth and real cost
A depth limit rejects long chains of nested fields, but a broad query with many aliases or large lists can still be expensive. Conversely, a deeper lookup with a unique ID may be cheap. Combine a depth bound with limits on aliases, breadth, operation count, list page size, and estimated cost. Assign weights to expensive resolvers and multiply by worst-case list sizes when estimating work.
Measure the model against production-like data. If the estimate says a query is cheap but it causes hundreds of database calls, fix the resolver or its weight. Batch and cache where appropriate while retaining tenant and permission scope. A cost rule should reject excessive work before execution. Timeouts and concurrency limits provide a backstop for underestimated work, not a substitute for estimating it.
Test alias and fragment amplification
Repeated aliases can call the same expensive field many times in one HTTP request. Fragments can make this repetition less obvious in a quick visual review. Generate bounded test queries that repeat a costly resolver, then verify the server counts the total planned work and rejects excess before downstream services run. Keep a legitimate complex query in the suite to detect rules that block normal clients.
If batching multiple operations is supported, decide how their costs combine and whether authentication context is shared safely. Apply a per-account or per-client quota across requests; a single-query cost ceiling will not stop thousands of moderate queries. Test an attacker spreading requests across connections and a genuine client recovering from a rejected request.
Protect special execution paths
Introspection itself can produce large responses on a big schema. Apply suitable depth, size, and rate controls to introspection operations if they are enabled. Subscriptions can hold connections open and fan events to many clients; cap active subscriptions and recheck permission when access changes. Persisted or trusted documents can narrow accepted operations for controlled clients, but maintain a safe rollout process when client queries change.
A federated graph may distribute one query across several subgraphs. Include downstream fan-out and timeout budgets in the cost model. Make sure the gateway or router does not assume a field is cheap simply because its top-level selection is small. Trace a representative expensive query end to end, including error paths and cancellation.
Return safe errors and observable limits
When a query exceeds policy, return a stable client-facing error without exposing internal resolver names, database plans, or stack traces. Record the rule, estimated cost, actual duration where available, and correlation ID in restricted telemetry. Avoid logging complete query variables if they may contain personal data or secrets. Monitor both rejected operations and expensive accepted ones.
Tune limits with a sample of real client operations. A sudden rise in rejection can reflect a new legitimate query, a schema change, or abuse. Keep a documented exception path with an owner and expiry, and prefer adjusting the operation or resolver cost before raising a global ceiling. A global increase affects every client, including attackers.
Verify the correct tool boundary
APISAST scans OpenAPI and Swagger files. It does not parse native GraphQL queries, introspection results, or resolver logic. An HTTP endpoint described in OpenAPI may have contract-level findings, but those findings do not establish GraphQL depth or field security. Use GraphQL-aware validators and integration tests for this topic.
Review introspection policy whenever a new client or partner needs schema access. Review cost weights whenever a field starts calling a new service or returning a larger list. These two reviews answer different questions: who can learn the graph's shape, and how much work a permitted query may cause.
Example policy for a public graph
A public GraphQL service might allow introspection for developer tooling but limit it by account and response size. It can cap ordinary operations at a measured cost, enforce a smaller maximum for nested lists, and set an account-level budget over a short interval. A first-party mobile application might use reviewed trusted documents for its common screens, while third-party clients retain an open query interface with stricter limits.
Document which operations are rejected and why. Test a harmless schema query, a large introspection request, a deep nested list, a shallow query with many aliases, and a legitimate complex client screen. Verify that rejections occur before expensive resolvers run and that accepted requests stay within the intended resource budget. This makes the policy observable and avoids treating a hidden schema as the only defence. Record how the gateway identifies the caller and whether anonymous traffic receives a different budget. Repeat the test after a schema change that adds a new list field, since its default page size can change the true cost even if the query text stays the same.
Continue the work
Use these guides to put the checks into your API review process:
Primary reference: GraphQL security guidance.
APISAST reviews OpenAPI and Swagger contracts for documented design signals. Confirm authorization, enforcement, performance, and abuse controls against a running service.
More API security guides