GraphQL VAPT: Testing Query Complexity and Authorization
June 18, 2026
GraphQL has moved from niche technology to mainstream production use over the last five years, and VAPT methodologies designed around REST APIs consistently underweight the GraphQL-specific attack surface. The differences are not merely syntactic — GraphQL introduces new failure modes around query complexity, authorization enforcement, and resource control that require distinct testing approaches. This document describes how defensive VAPT programmes should assess GraphQL APIs.
Why GraphQL Requires Different Testing
REST APIs expose distinct endpoints for distinct resources, and each endpoint has explicit inputs and outputs that VAPT can enumerate systematically. GraphQL exposes a single endpoint that accepts arbitrary queries against a schema; the effective attack surface is not the endpoint but the entire schema and the resolvers that back it. A GraphQL VAPT that treats the single endpoint like a REST endpoint misses the majority of the surface it should be testing.
GraphQL's design goal — permitting clients to request exactly the fields they need in a single request — creates a tension with authorization and resource management. Every field can potentially be a permission check point, every query can traverse resource-intensive resolvers, and every field can potentially expose data that should be restricted. The complexity of correctly implementing authorization and resource controls across a large schema is substantial, and assessments consistently find gaps.
The introspection feature — GraphQL's built-in schema query capability — is invaluable during development and assessment but is frequently misconfigured for production. Assessments should identify whether introspection is enabled, whether it should be enabled for the environment being tested, and if disabled whether the disabling is enforced correctly (some implementations disable introspection by default but permit it for authenticated requests, exposing schema to any user who can authenticate).
Query Complexity and Denial of Service
GraphQL permits deeply nested queries and queries that request large numbers of fields in a single request. Without complexity controls, a single query can consume disproportionate server resources — CPU, memory, database queries — to construct the response. A defensive VAPT assessment tests whether complexity controls are in place and whether they effectively bound resource consumption per request.
Query depth limits are the simplest complexity control and are widely supported by GraphQL server libraries. The limit caps the maximum nesting depth of queries; queries exceeding the limit are rejected without execution. Depth limits alone are insufficient because a single-level query with many fields can still be expensive, but they eliminate the most extreme cases of nested traversal. Assessments should identify whether depth limits are configured and what the configured limit is.
Query cost analysis — assigning a cost to each field based on its resolver characteristics and rejecting queries whose total cost exceeds a threshold — is more sophisticated and provides better resource control. Assessments evaluate whether cost analysis is implemented, whether the cost model reflects actual resource consumption, and whether the threshold is set at a level that permits legitimate use while blocking abusive queries. A well-implemented cost analysis makes complexity-based denial-of-service structurally difficult.
Batching and Alias Abuse
GraphQL query batching permits multiple queries to be submitted in a single HTTP request. This is a legitimate feature for reducing network overhead but can be abused to bypass rate limits that count HTTP requests without counting the queries within them. Assessments should test whether rate limiting applies per query rather than per request; if it applies per request, a single batched request can perform an arbitrarily large number of operations under the rate limit.
Field aliasing — GraphQL's syntax for renaming fields in the response — can be used to request the same field multiple times in a single query. This bypasses rate limiting or resource controls that count unique fields but not repeated field invocations. Combined with expensive fields (fields whose resolvers make external API calls or database queries), aliasing can amplify resource consumption per query dramatically. Assessments should test whether resolvers handle repeated invocations efficiently and whether cost analysis correctly accounts for aliased fields.
Combined batching and aliasing produces an amplification factor that can defeat rate limiting entirely. A single HTTP request containing a batch of queries, each with heavily aliased expensive fields, can consume orders of magnitude more resources than a naive rate limit would permit. Assessments should test this combination explicitly rather than testing each independently; the combined effect frequently reveals gaps that individual tests miss.
Field-Level Authorization Enforcement
GraphQL's field-per-permission model requires that authorization is enforced at the field level rather than only at the query entry point. A user authorised to query a customer object may not be authorised to access every field on that customer object; the authorization system must decide whether to include, redact, or reject requests for specific fields based on the requesting user's permissions. Implementations that enforce authorization only at the query entry point systematically expose fields the user should not access.
A defensive assessment tests authorization enforcement systematically across the schema. For each type in the schema, the assessment identifies which fields have documented access restrictions, then tests whether those restrictions are enforced by attempting to access the restricted fields with credentials that should not have permission. This is mechanical work that scales poorly with manual testing but is well-suited to automated VAPT platforms that can systematically enumerate the schema and generate test queries.
Authorization enforcement should also be tested against introspection-informed enumeration. An attacker who has schema access via introspection can systematically enumerate every type and field, then test each for authorization gaps. A defensive assessment should perform the same enumeration and identify fields that lack authorization enforcement before the assessment output. Fields that are "never intended to be queried by external users" but are reachable via the public schema without authorization enforcement are recurring findings.
Mutation Authorization and Business Logic
GraphQL mutations — the write operations — require the same authorization rigour as queries but also require business logic validation that queries typically do not. A mutation may be authorised for the requesting user but produce a state change that violates business invariants (transferring more than the source account balance, ordering a product with a negative quantity, updating a record to reference a resource the user does not have access to). Assessments should test business logic enforcement in mutations independently from authorization enforcement.
Argument validation is a common gap in mutation implementations. GraphQL type validation ensures arguments have the correct scalar types but does not validate business constraints. An argument typed as String may accept arbitrary length strings, an argument typed as Int may accept negative values or values outside the intended range, and enumerated types may accept values that were legal in a previous schema version but should no longer be accepted. Assessments should test argument bounds explicitly.
Race conditions in mutations frequently produce business logic issues that are invisible to sequential testing. A mutation that reads a value, validates it, and writes a modified value can be exploited by parallel submissions that read the same value before either has completed the write. Assessments should test critical mutations for race condition resistance by submitting parallel requests and observing whether the resulting state is consistent with any legal serialisation of the requests.
Persistent Queries and Production Hardening
Persistent queries — a mechanism where clients register queries in advance and reference them by identifier at runtime — provide substantial security benefits when adopted for production traffic. The server only executes registered queries; arbitrary queries submitted by external users are rejected. This eliminates a large fraction of the query complexity and authorization surface because the query set the server will execute becomes finite and reviewable.
Assessments should evaluate whether persistent queries are used for external-facing GraphQL APIs and whether the query registration process is protected against submission of malicious queries. Systems that permit arbitrary clients to register queries and then execute them provide no security benefit over accepting arbitrary queries directly. The security benefit comes from restricting query registration to trusted development processes.
For GraphQL APIs consumed by first-party clients only, persistent queries can be enforced strictly with zero operational cost. For APIs that must accept arbitrary queries from third-party integrations, persistent queries are not applicable, but the other controls — depth limits, cost analysis, field-level authorization, batching-aware rate limits — become correspondingly more important. Assessments should evaluate the control set against the API's intended consumption model rather than against a single reference configuration.