# GraphQL security: authorize resolvers and bound query work

> Review GraphQL field authorization, query complexity, batching, pagination and safe validation for developer-facing APIs.

Official source: https://warhoundai.com/blog/graphql-security-resource-budgets/
Published: 2026-10-26
Author: WarHoundAI
Updated: 2026-11-12

## One endpoint can represent many operations

GraphQL fundamentally alters the attack surface of an application by shifting from discrete routing endpoints to a single, highly flexible operation hub. A single HTTP endpoint can reach many resolvers, relationships, and backend services, making traditional endpoint-based access control insufficient. Treat the schema as a map of available operations and examine the access rules for each sensitive object and field. Restricting the endpoint to authenticated users is only the beginning, because users can still request another tenant's data through an insufficiently protected resolver.

According to the OWASP GraphQL Cheat Sheet, the main design of GraphQL involves users supplying identifiers to backend data fetchers. This means user input is directly included in database queries or downstream HTTP requests. Application engineers must apply strict input validation, utilizing specific GraphQL data types like scalars or enums, and writing custom validators for complex inputs. Gracefully rejecting invalid input without revealing excessive internal API mechanics is a critical first step in defending the single endpoint.

## Authorize at the data boundary

Authorization must happen at the resolver or business logic layer. Relying solely on possessing an object identifier is a common failure mode known as Broken Object Level Authentication. The OWASP guidance emphasizes enforcing authorization checks on both graph edges and nodes. If an API exposes a generic node lookup field, an attacker might bypass edge-level checks by querying the node directly with a known or guessed identifier.

Check the current actor's permission when accessing an object, including nested relationships and mutation targets. Apply a consistent policy in the trusted data-access layer so a second resolver cannot bypass the restriction. A loader cache must not mix results across users or tenants when those results depend on authorization. Avoid caching a privileged response under an identifier that is later reused for a less privileged request in the same shared cache. Engineers should use interfaces and unions to create structured, hierarchical data types that return properties strictly according to the requester's permissions.

## Mitigating query batching attacks

GraphQL natively supports query batching, allowing a client to submit an array of queries in a single HTTP request. While designed for performance, attackers abuse this feature to perform brute-force attacks against authentication endpoints or enumerate identifiers while bypassing network-level rate limits. Because the payload is delivered in a single HTTP request, traditional web application firewalls and reverse proxies that count requests per IP address will fail to detect or block the attack.

To mitigate batching attacks, the OWASP GraphQL Cheat Sheet recommends implementing object request rate limiting in the application code. This tracks how many different object instances a caller requests within a timeframe, blocking them if they exceed the threshold regardless of how the requests are batched. A significant implementation tradeoff is the added state management overhead. Alternatively, engineers can explicitly prevent batching for sensitive operations, such as password validation or one-time password checks, forcing attackers to make discrete network calls that trigger standard infrastructure rate limits.

## Bound work before running the query

Request bytes alone are a poor estimate of GraphQL execution cost. Nested relationships, aliases, and large list arguments can multiply backend work, leading to application-level denial of service. A shallow operation can still request a very expensive collection if pagination is unbounded. OWASP recommends implementing depth limiting, amount limiting, and pagination to restrict the volume of data returned in a single response.

A common failure mode is relying exclusively on infrastructure timeouts. Infrastructure timeouts are often inaccurate and activate only after a malicious query has already consumed excessive database resources. Instead, implement query cost analysis and application-layer timeouts. Query cost analysis assigns a weight to specific fields and rejects queries that exceed a maximum allowed cost before execution begins. The tradeoff is the engineering effort required to accurately profile and assign costs to every field in a large schema. Teams should start with strict depth and pagination limits before investing in full query cost analysis.

## Keep errors and operational controls deliberate

Return errors that help a client correct a request without exposing database queries, credentials, or internal stack traces. By default, many GraphQL implementations return verbose error messages that assist attackers in mapping the backend architecture. Decide how schema discovery should work for the intended audience. Disabling introspection in production is a standard recommendation for internal APIs, but it is not a complete authorization control.

Even with introspection disabled, attackers can guess fields. Furthermore, some GraphQL implementations include a feature that returns a hint when a requested field name is similar to an existing field. OWASP advises disabling these suggestion hints if introspection is disabled to decrease exposure. If persisted queries are appropriate for a first-party client, keep their registration and deployment process controlled rather than accepting arbitrary query registrations from every user, which defeats the purpose of an allowlist.

## Build a safe resolver test matrix

To validate authorization and resource limits, engineering teams should construct a safe test matrix using synthetic fixtures. Use an isolated environment with defined limits rather than attempting denial-of-service experiments against production. A hypothetical test matrix for a multi-tenant application might include two synthetic tenants, Tenant Alpha and Tenant Beta, with distinct user principals.

The matrix must define the query shape, the principal executing the query, the expected policy, and the observed outcome. For example, a test case where Tenant Alpha requests Tenant Alpha's invoice list should result in an allowed read. A test case where Tenant Alpha requests Tenant Beta's specific invoice ID via a direct node lookup must result in a denied outcome or a not-found error. To test resource budgets, a synthetic query requesting ten levels of nested relationships should be rejected by the depth limiter before database execution begins. Record these expected allowed and denied outcomes in automated integration tests to prevent regressions.

## Sources and acknowledgments

Drafted with Gemini and checked for source links, advisory identifiers, structure and unsupported product claims. Examples are synthetic and are not scan results.

Drafted with Gemini and checked for source links, advisory identifiers, structure and unsupported product claims. Examples are synthetic and are not scan results.

- [OWASP GraphQL Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/GraphQL_Cheat_Sheet.html)
