Security Cipher

Security Resources

⌘K
  1. Home
  2. Security Resources
  3. Vulnerability Explain
  4. GraphQL Security

GraphQL Security

This Vulnerability Explain post covers GraphQL Security. GraphQL gives clients enormous flexibility to query exactly what they want, but that flexibility introduces its own class of risks – from schema leakage to denial of service. Let’s look at the common GraphQL pitfalls and how to address them.

What is GraphQL Vulnerabilities?

GraphQL vulnerabilities are weaknesses specific to GraphQL APIs, including exposed introspection, missing authorization on fields and resolvers, query depth and batching abuse, and injection through arguments.

Because a single endpoint serves arbitrary client-defined queries, problems differ from REST. An attacker can map the entire schema via introspection, request deeply nested relationships to exhaust resources, or reach fields the UI never exposes because authorization was only applied at the UI level.

How does GraphQL Vulnerabilities work?

GraphQL-specific risks generally fall into these patterns:

  • Introspection Exposure
    • Production introspection lets an attacker download the full schema, including hidden types and fields.
  • Broken Field Authorization
    • Authorization is enforced inconsistently, so some resolvers return data the user should not see.
  • Query Depth and Complexity Abuse
    • Deeply nested or expensive queries overload the server, causing denial of service.
  • Batching and Aliases
    • Attackers use aliases and batched operations to brute-force or amplify requests, bypassing simple rate limits.
  • Injection via Arguments
    • Unsanitized arguments flow into backend queries, enabling SQL or NoSQL injection.

GraphQL’s power is its risk: it trusts clients to ask for anything. Authorization per field, limits on complexity, and locking down introspection bring that power back under control.

GraphQL security diagram showing introspection and a deeply nested query overloading the API

How GraphQL attacks work: introspection exposes the schema and nested queries overload the server.

Tools and Techniques for GraphQL Vulnerabilities Testing

Testing leans on schema exploration and crafting expensive or unauthorized queries.

Manual Testing Methodologies

  • Introspection Query – Run the introspection query to dump the schema and find hidden operations.
  • Authorization Probing – Request sensitive fields and mutations as low-privilege users.
  • Depth and Complexity – Build deeply nested queries to test for resource exhaustion.
  • Alias and Batch Abuse – Use aliases to repeat operations and test rate-limit bypass.

Automated Scanning Tools

  • GraphQLmap – Interacts with and attacks GraphQL endpoints.
  • BatchQL – Tests batching, aliasing, and introspection issues.
  • graphql-cop – Audits GraphQL APIs for common misconfigurations.
  • graphw00f – Fingerprints GraphQL engines to guide testing.

GraphQL Vulnerabilities Protection Mechanisms

Best Practices for Secure Coding

  • Authorize Every Resolver
    • Description: Enforce access control at the field and resolver level, not just the UI.
    • Benefits: Hidden fields stay protected.
    • Implementation Tip: Check permissions inside each resolver or via a consistent authorization layer.
  • Limit Depth and Complexity
    • Description: Cap query depth, complexity, and the number of returned items.
    • Benefits: Prevents resource-exhaustion denial of service.
    • Implementation Tip: Use complexity analysis and pagination limits.
  • Disable Introspection in Production
    • Description: Turn off introspection and verbose errors in production.
    • Benefits: Reduces schema exposure.
    • Implementation Tip: Allow introspection only in development environments.

Best Practices for Organizations

  • Rate Limiting and Cost Control
    • Apply per-operation cost limits and rate limiting.
    • Account for batching and aliases in limits.
  • Safe Backends
    • Use parameterized backend queries from resolvers.
    • Validate all arguments.
  • Testing
    • Add GraphQL-specific tests to security reviews.
    • Re-audit when the schema changes.

Top GraphQL Vulnerabilities payloads used by Security Researchers

As a security researcher, knowing the most common payloads helps you detect and prevent these attacks. Use this knowledge ethically and only on systems you are authorized to test. Some sample payloads are shown below.

// Introspection query to dump the schema
{ __schema { types { name fields { name } } } }
// Deeply nested query for resource exhaustion
{ user { friends { friends { friends { friends { name } } } } } }
// Alias-based amplification / brute force
{ a: login(p:"1"){ok} b: login(p:"2"){ok} c: login(p:"3"){ok} }
// Injection via an argument
{ users(filter: "' OR '1'='1") { id email } }

Real-World Example: Schema Dump to Hidden Mutation

A GraphQL API left introspection enabled in production and enforced authorization only in the web front-end.

An attacker ran the introspection query, downloaded the full schema, and discovered an undocumented deleteUser mutation. Because authorization was never enforced in the resolver, calling it directly as a normal user deleted arbitrary accounts.

The fix disabled introspection in production, added resolver-level authorization, and capped query complexity. GraphQL is powerful, but every field and resolver must enforce its own rules – the client cannot be trusted to ask nicely.

Vulnerable and secure code of GraphQL Vulnerabilities

The following example shows the contrast between vulnerable and secure code for GraphQL Vulnerabilities. It helps you see how the flaw creeps into real code and the changes that shut it down.

🥺 Vulnerable Code:

// Vulnerable: introspection on, no resolver authorization
const server = new ApolloServer({
  typeDefs,
  resolvers: {
    Mutation: {
      // Any caller can run this - authorization only existed in the UI
      deleteUser: (_, { id }) => db.deleteUser(id),
    },
  },
  introspection: true,            // schema is downloadable in production
});
  • Introspection lets anyone map the full schema, including hidden mutations.
  • The resolver performs no authorization, so any user can delete accounts.

😎 Secure Code:

// Secure: resolver authorization, introspection off, complexity limits
const server = new ApolloServer({
  typeDefs,
  resolvers: {
    Mutation: {
      deleteUser: (_, { id }, ctx) => {
        if (!ctx.user || ctx.user.role !== "admin") {
          throw new ForbiddenError("Not allowed");
        }
        return db.deleteUser(id);
      },
    },
  },
  introspection: process.env.NODE_ENV !== "production",
  validationRules: [depthLimit(7), createComplexityLimitRule(1000)],
});
  • Each resolver enforces authorization independently of the UI.
  • Introspection is disabled in production and depth/complexity limits stop query DoS.

Conclusion

GraphQL trades REST’s rigidity for client-defined flexibility, and that flexibility is exactly what attackers exploit. Authorize every resolver and field, disable introspection and verbose errors in production, cap query depth and complexity, rate-limit with batching in mind, and parameterize backend queries. Treat each resolver as its own protected endpoint and GraphQL stays as safe as it is powerful.

How can we help?