GraphQL pentesting: common vulnerabilities in GraphQL APIs
GraphQL solves real problems with REST APIs — over-fetching, under-fetching, versioning — but introduces a different attack surface that REST-oriented tests don't cover well. A single endpoint, a strongly-typed schema and client-defined nested queries completely change what needs testing and how.
Why GraphQL has a different attack surface than REST
In REST, each endpoint exposes one specific operation and access control is usually applied per route. In GraphQL there's a single endpoint (typically `/graphql`) where the client composes the query: it decides which fields, which nested relations and what depth to resolve. That shifts most of the access-control and resource-limiting logic onto each individual resolver, and it's easy for some resolver to end up without the same checks as the rest — a surface problem that's far harder to map automatically than in REST.
Unrestricted introspection: the most common finding
GraphQL includes, by design, an introspection feature that lets you query the full API schema: every type, field, mutation and their relations. It's essential during development, but leaving it enabled in production is equivalent to publishing your internal API's full documentation to anyone who asks for it. It's by far the most repeated finding in GraphQL pentesting: it lets an attacker map sensitive fields and mutations (`isAdmin`, `internalNotes`, `deleteUser`) in seconds — fields that were never publicly documented but exist in the schema.
Batching attacks and resource exhaustion (query-level DoS)
Because the client composes the query, it can build arbitrarily deep nested queries (alias batching, circular queries across related types) that force the server to execute thousands of internal resolutions from a single HTTP request — invisible to a WAF that only counts requests per second. Without depth limiting, query cost analysis or a cap on aliases per request, a single well-crafted query can take down the service or exhaust the database. It's a denial-of-service vector that doesn't exist in the same form in REST.
BOLA/IDOR in GraphQL resolvers
Broken object-level authorization (BOLA, #1 on the OWASP API Security Top 10) is just as relevant in GraphQL as in REST, but easier to overlook: every resolver that returns an object by ID must independently check that the authenticated user has permission over that specific object. In large APIs with dozens of resolvers, it's common for some to inherit the authentication check (are you logged in?) but not the authorization check (can you see THIS resource?) — allowing access to other users' data just by changing the ID in the query.
FAQ
Is disabling introspection in production enough to be secure?
No. Disabling introspection reduces easy reconnaissance surface, but doesn't fix access-control or resource-exhaustion issues — an attacker with partial schema knowledge (e.g. from client-side code) can still exploit vulnerable resolvers without introspection.
Does an automated scanner detect these GraphQL vulnerabilities?
It partially detects exposed introspection and some known patterns, but BOLA in specific resolvers and query complexity limits require understanding the business logic of each mutation and relation — that's manual pentesting territory, not automated scanning.