GraphQL Vulnerabilities

GraphQLGraphQL DoSGraphQL InjectionIntrospection

GraphQL Vulnerabilities at a glance

What it is: Security vulnerabilities specific to GraphQL APIs including excessive data exposure through nested queries, denial of service via query complexity, missing authorization on fields, and introspection-based reconnaissance.
Why it happens: GraphQL vulnerabilities occur when APIs lack query limits, input validation, or field-level authorization, allow introspection in production, or have insecure resolver logic enabling injection or performance abuse.
How to fix: Enforce query depth and complexity limits, validate and sanitize resolver inputs, apply field-level authorization, and disable introspection in production.

Overview

GraphQL is a powerful query language for APIs that allows clients to request exactly the data they need. However, this flexibility introduces unique security challenges. Unlike REST APIs with fixed endpoints, GraphQL's single endpoint and flexible queries create new attack surfaces.

Common GraphQL vulnerabilities include deeply nested queries causing denial of service, missing authorization at the field level allowing unauthorized data access, introspection queries revealing the entire API schema, batching attacks amplifying request impact, and injection vulnerabilities in resolvers. The recursive nature of GraphQL queries means a single malicious query can trigger thousands of database calls or expose vast amounts of data.

sequenceDiagram participant Attacker participant API as GraphQL API participant DB as Database Attacker->>API: POST /graphql<br/>{user{posts{author{posts{author{posts{...}}}}}} API->>DB: Query user DB-->>API: User data loop 100x nested levels API->>DB: Query posts for each author DB-->>API: Posts data end API->>API: Out of memory API-->>Attacker: 500 Server Error Note over API: Missing: Query depth limits<br/>Missing: Complexity analysis
A potential flow for a GraphQL Vulnerabilities exploit

Where it occurs

GraphQL vulnerabilities occur in APIs lacking query depth or complexity limits, field-level authorization, or input validation, and in resolvers vulnerable to injection or excessive data fetching.

Impact

GraphQL vulnerabilities lead to denial of service through query complexity attacks, excessive data exposure bypassing intended access controls, complete API schema disclosure through introspection, database overload from N+1 queries, injection attacks in resolvers, and information disclosure through error messages. A single malicious query can bring down an entire service or exfiltrate sensitive data.

Prevention

Prevent GraphQL abuse by enforcing depth and complexity limits, validating resolver inputs, using field-level auth, disabling introspection, allowlisting or persisting queries, parameterizing DB access, limiting batches, and logging query activity.

Examples

Switch tabs to view language/framework variants.

GraphQL API allows deeply nested queries causing DoS

No query depth limits enable resource exhaustion.

Vulnerable
JavaScript • Apollo Server — Bad
const { ApolloServer } = require('apollo-server');

const typeDefs = `
  type User {
    id: ID!
    posts: [Post!]!
  }
  type Post {
    author: User!
  }
`;

const server = new ApolloServer({
  typeDefs,
  resolvers
  // BUG: No depth limit or complexity analysis
});
  • Line 16: No protection against deep queries

Deep nested queries can cause exponential database queries and resource exhaustion.

Secure
JavaScript • Apollo Server — Good
const { ApolloServer } = require('apollo-server');
const { createComplexityLimitRule } = require('graphql-validation-complexity');

const server = new ApolloServer({
  typeDefs,
  resolvers,
  validationRules: [
    createComplexityLimitRule(1000, {
      onCost: (cost) => console.log('Query cost:', cost)
    })
  ],
  plugins: [
    {
      requestDidStart() {
        return {
          didResolveOperation({ request }) {
            const depth = getQueryDepth(request.query);
            if (depth > 5) {
              throw new Error('Query too deep');
            }
          }
        };
      }
    }
  ]
});
  • Line 7: Complexity limit rule
  • Line 17: Depth checking

Implement both complexity analysis and depth limits to prevent DoS.

Engineer Checklist

  • Implement query depth limits (max 5-7 levels)

  • Calculate and enforce query complexity scores

  • Add field-level authorization in resolvers

  • Disable introspection in production

  • Implement DataLoader to prevent N+1 queries

  • Limit batch operation sizes

  • Validate all resolver inputs

  • Use parameterized queries in resolvers

  • Implement query cost analysis

  • Add rate limiting on the GraphQL endpoint

  • Avoid exposing stack traces in errors

  • Log all GraphQL queries

  • Use persisted/allowed queries in production

  • Implement timeout limits on resolver execution

  • Test with complex nested queries

End-to-End Example

A GraphQL API has no query depth limits, allowing attackers to craft deeply nested queries that cause exponential database calls and service outage.

Vulnerable
JAVASCRIPT
// Vulnerable: No query limits
const { ApolloServer, gql } = require('apollo-server');

const typeDefs = gql`
  type User {
    id: ID!
    name: String
    posts: [Post]
  }
  
  type Post {
    id: ID!
    title: String
    author: User
  }
  
  type Query {
    user(id: ID!): User
  }
`;

const resolvers = {
  Query: {
    user: (_, { id }) => getUserById(id)  // No authorization
  },
  User: {
    posts: (user) => getPostsByUser(user.id)  // N+1 problem
  },
  Post: {
    author: (post) => getUserById(post.authorId)  // Allows deep nesting
  }
};

const server = new ApolloServer({ typeDefs, resolvers });
Secure
JAVASCRIPT
// Secure: Query limits and authorization
const { ApolloServer, gql } = require('apollo-server');
const depthLimit = require('graphql-depth-limit');
const { createComplexityLimitRule } = require('graphql-validation-complexity');
const DataLoader = require('dataloader');

const typeDefs = gql`
  type User {
    id: ID!
    name: String
    posts: [Post]
  }
  
  type Post {
    id: ID!
    title: String
    author: User
  }
  
  type Query {
    user(id: ID!): User
  }
`;

// DataLoader to prevent N+1
const userLoader = new DataLoader(async (ids) => {
  return await getUsersByIds(ids);
});

const resolvers = {
  Query: {
    user: async (_, { id }, context) => {
      // Field-level authorization
      if (!context.user) {
        throw new Error('Unauthorized');
      }
      return userLoader.load(id);
    }
  },
  User: {
    posts: async (user, _, context) => {
      // Check authorization for user's posts
      if (context.user.id !== user.id && !context.user.isAdmin) {
        throw new Error('Unauthorized');
      }
      return getPostsByUser(user.id);
    }
  },
  Post: {
    author: (post) => userLoader.load(post.authorId)
  }
};

const server = new ApolloServer({
  typeDefs,
  resolvers,
  validationRules: [
    depthLimit(5),  // Max 5 levels deep
    createComplexityLimitRule(1000, {  // Max complexity score
      scalarCost: 1,
      objectCost: 10,
      listFactor: 10
    })
  ],
  introspection: process.env.NODE_ENV !== 'production',  // Disable in prod
  context: ({ req }) => ({
    user: authenticateUser(req)
  }),
  formatError: (error) => {
    // Don't expose internal errors
    if (error.message.startsWith('Database')) {
      return new Error('Internal server error');
    }
    return error;
  }
});

Discovery

Test with introspection queries to map the schema. Craft deeply nested queries and observe performance degradation or errors.

  1. 1. Test GraphQL introspection

    http

    Action

    Query GraphQL schema using introspection

    Request

    POST https://api.example.com/graphql
    Body:
    "{__schema{types{name,fields{name}}}}"

    Response

    Status: 200
    Body:
    {
      "note": "Complete API schema revealed including all types and fields"
    }

    Artifacts

    api_schema field_list type_definitions
  2. 2. Test query depth limits

    http

    Action

    Send deeply nested query to test depth restrictions

    Request

    POST https://api.example.com/graphql
    Body:
    "{user{posts{author{posts{author{posts{author{posts{author{name}}}}}}}}}}"

    Response

    Status: 200
    Body:
    {
      "note": "Deep query executes, causing thousands of database queries"
    }

    Artifacts

    n_plus_one_queries performance_degradation
  3. 3. Test field-level authorization

    http

    Action

    Request unauthorized fields to test access controls

    Request

    POST https://api.example.com/graphql
    Body:
    "{users{email,password,ssn}}"

    Response

    Status: 200
    Body:
    {
      "note": "Sensitive fields accessible without proper authorization"
    }

    Artifacts

    authorization_bypass sensitive_data

Exploit steps

Attacker uses introspection to discover the schema, then crafts deeply nested or complex queries to cause DoS or extract excessive data.

  1. 1. Map complete API via introspection

    Schema discovery

    http

    Action

    Extract complete GraphQL schema and identify attack surface

    Request

    POST https://api.example.com/graphql
    Body:
    "{__schema{queryType{fields{name,args{name,type{name}}}}}}"

    Response

    Status: 200
    Body:
    {
      "note": "Full API structure revealed including hidden queries"
    }

    Artifacts

    complete_schema query_list hidden_endpoints
  2. 2. Execute query depth DoS attack

    Resource exhaustion via nesting

    http

    Action

    Send maximally nested query to exhaust server resources

    Request

    POST https://api.example.com/graphql
    Body:
    "{user{posts{comments{author{posts{comments{author{posts{...}}}}}}}}"

    Response

    Status: 200
    Body:
    {
      "note": "Service crashes or becomes unresponsive"
    }

    Artifacts

    service_outage cpu_exhaustion database_overload
  3. 3. Extract unauthorized data via field injection

    Mass data exfiltration

    http

    Action

    Query all users with sensitive fields lacking authorization

    Request

    POST https://api.example.com/graphql
    Body:
    "{users{id,email,passwordHash,ssn,creditCard}}"

    Response

    Status: 200
    Body:
    {
      "note": "All user PII and credentials extracted"
    }

    Artifacts

    user_database password_hashes pii

Specific Impact

Service outage from query complexity DoS, unauthorized data access through missing field-level authorization, and complete API schema disclosure.

Fix

Implement query depth limits to prevent deeply nested queries. Use complexity analysis to limit expensive queries. Add field-level authorization in resolvers. Use DataLoader to prevent N+1 queries. Disable introspection in production. Sanitize error messages to avoid information disclosure.

Detect This Vulnerability in Your Code

Sourcery automatically identifies graphql vulnerabilities vulnerabilities and many other security issues in your codebase.

Scan Your Code for Free