GraphQL query depth and complexity limits
Why it matters
GraphQL without query depth and complexity limits is a denial-of-service vector with a trivially crafted payload: a malicious query that nests deeply-related types exponentially expands server-side resolution work while staying small on the wire (CWE-770, CWE-400). OWASP API 2023 flags this as unrestricted resource consumption (API4). A single deeply-nested query can saturate database connections and CPU for seconds, affecting all concurrent users. Without introspection controls, attackers also gain a complete map of your schema for free.
Severity rationale
High because a single malicious query with exponential field nesting can exhaust server resources and produce a denial-of-service condition against all users of the API.
Remediation
Add depth and complexity limits to your GraphQL server configuration before accepting public traffic. For Apollo Server:
import depthLimit from 'graphql-depth-limit'
import { createComplexityLimitRule } from 'graphql-validation-complexity'
const server = new ApolloServer({
typeDefs,
resolvers,
validationRules: [
depthLimit(6),
createComplexityLimitRule(1000),
],
})
For GraphQL Yoga, use the built-in useDepthLimit() and useQueryComplexity() plugins. Set max depth to 5-7 (enough for most legitimate queries) and tune complexity to allow your most expensive real query with 20-30% headroom. Disable introspection in production if your API is not public: introspection: process.env.NODE_ENV !== 'production'.
Detection
- ID:
graphql-depth-limits - Severity:
high - What to look for: If GraphQL is detected, enumerate all GraphQL server configurations and check whether query depth limits and complexity limits are enforced. Look for:
graphql-depth-limitpackage usage,graphql-query-complexitypackage, Apollo ServervalidationRuleswith depth/complexity plugins, Yoga/Pothos built-in complexity config, or manual depth checking. Without these limits, a malicious query can nest deeply enough to exhaust server resources. - Pass criteria: At least one of: query depth limit (e.g., max depth of 5-10) OR query complexity limit is enforced at the GraphQL server layer before execution.
- Fail criteria: GraphQL server accepts arbitrarily deep or complex queries with no validation rules applied.
- Skip (N/A) when: GraphQL is not used in the project. Signal: no
graphql,@apollo/server,graphql-yoga,pothos-graphql,nexus, ortype-graphqlinpackage.json; no.graphqlschema files; no GraphQL endpoint in routes. - Detail on fail: Example:
Apollo Server configured with no validation rules. Note what's missing (e.g., "Apollo Server configured with no validation rules; graphql-depth-limit and graphql-query-complexity not in dependencies"). Max 500 chars. - Remediation: Add query depth and complexity limits to your GraphQL server configuration in
src/orapp/api/graphql/. For Apollo Server, use thegraphql-depth-limitpackage:validationRules: [depthLimit(5)]. For complexity limiting, usegraphql-query-complexitywith a max complexity threshold appropriate to your schema. For GraphQL Yoga, use the built-inuseDepthLimit()anduseQueryComplexity()plugins. A reasonable starting point is max depth of 5-7 and a complexity limit that allows your most complex legitimate query with 20-30% headroom. Also consider disabling introspection in production if your API is not public.
External references
- cwe · CWE-770 — Allocation of Resources Without Limits or Throttling
- cwe · CWE-400 — Uncontrolled Resource Consumption
- owasp:2021 · A04 — Insecure Design
Taxons
History
- 2026-04-18·v1.0.0·Initial import from saas-api-design·automated