GraphQL Security Vulnerabilities: The Complete Guide for 2025
Why GraphQL Security Is Different
GraphQL has become the API standard for modern frontends, powering companies like GitHub, Shopify, and Airbnb. But its flexible query language creates a fundamentally different attack surface than REST APIs.
| Attack Vector | REST API | GraphQL |
|---|---|---|
| Data enumeration | Limited by endpoints | Introspection reveals entire schema |
| DoS via queries | Rate limit per endpoint | Nested queries can exponentially grow |
| Batching attacks | One operation per request | Multiple operations in single request |
| Authorization | Per-endpoint middleware | Per-field resolver logic required |
| Information disclosure | Fixed response shapes | Client controls what's returned |
Key insight: The same flexibility that makes GraphQL great for developers makes it dangerous without proper security controls.
Vulnerability #1: Introspection Enabled in Production
GraphQL introspection lets anyone query your entire API schema — every type, field, mutation, and relationship.
❌ Vulnerable Configuration
// Apollo Server — introspection ON by default
const server = new ApolloServer({
typeDefs,
resolvers,
// No introspection setting = enabled by default!
});
What Attackers See
# This query dumps your entire schema
{
__schema {
types {
name
fields {
name
type { name }
}
}
mutationType {
fields {
name
args { name type { name } }
}
}
}
}
An attacker now knows every data model, relationship, mutation, and argument — a complete roadmap for exploitation.
✅ Fixed Configuration
const server = new ApolloServer({
typeDefs,
resolvers,
introspection: process.env.NODE_ENV !== 'production',
plugins: [
ApolloServerPluginLandingPageDisabled(),
],
});
Vulnerability #2: Query Depth Attacks (GraphQL Bombs)
Nested relationships let attackers craft queries that explode exponentially.
❌ Vulnerable Query
# Each level multiplies the database load
query {
users {
posts {
comments {
author {
posts {
comments {
author {
posts { # 7 levels deep — hundreds of DB queries
title
}
}
}
}
}
}
}
}
}
A single request like this can trigger thousands of database queries (the N+1 problem, multiplied).
✅ Fix: Query Depth Limiting
import depthLimit from 'graphql-depth-limit';
const server = new ApolloServer({
typeDefs,
resolvers,
validationRules: [depthLimit(5)], // Max 5 levels deep
});
Vulnerability #3: Query Complexity / Cost Attacks
Even shallow queries can be expensive if they request massive datasets.
❌ Expensive Query
query {
allUsers(first: 10000) { # 10,000 users
orders(first: 100) { # × 100 orders each
items { # × N items each
product {
reviews(first: 50) { # × 50 reviews each
text
}
}
}
}
}
}
✅ Fix: Query Cost Analysis
import { createComplexityLimitRule } from 'graphql-validation-complexity';
const server = new ApolloServer({
typeDefs,
resolvers,
validationRules: [
createComplexityLimitRule(1000, {
scalarCost: 1,
objectCost: 10,
listFactor: 20,
onCost: (cost) => {
console.log('Query cost:', cost);
},
}),
],
});
Vulnerability #4: Batching Attacks for Brute Force
GraphQL allows sending multiple operations in a single HTTP request — perfect for brute-forcing.
❌ Brute Force via Batching
[
{ "query": "mutation { login(email: \"admin@example.com\", password: \"password1\") { token } }" },
{ "query": "mutation { login(email: \"admin@example.com\", password: \"password2\") { token } }" },
{ "query": "mutation { login(email: \"admin@example.com\", password: \"password3\") { token } }" },
// ... 1000 more attempts in a single request
]
Traditional rate limiting (per request) won't catch this — it's one HTTP request with 1,000 login attempts.
✅ Fix: Limit Batch Size + Per-Operation Rate Limiting
// Limit batch size
app.use('/graphql', (req, res, next) => {
if (Array.isArray(req.body) && req.body.length > 5) {
return res.status(400).json({
error: 'Batch limit exceeded. Maximum 5 operations per request.'
});
}
next();
});
// Per-operation rate limiting on sensitive mutations
const rateLimitDirective = {
login: { window: '15m', max: 5 },
resetPassword: { window: '1h', max: 3 },
createUser: { window: '1h', max: 10 },
};
Vulnerability #5: Missing Field-Level Authorization
With REST, you authorize at the endpoint level. With GraphQL, every field needs authorization.
❌ Vulnerable Resolver
const resolvers = {
Query: {
user: (_, { id }) => User.findById(id), // No auth check!
},
User: {
email: (user) => user.email, // Anyone can see emails
ssn: (user) => user.ssn, // Anyone can see SSNs!
salary: (user) => user.salary, // Anyone can see salaries!
internalNotes: (user) => user.notes, // Internal data exposed
},
};
✅ Fixed: Field-Level Auth
const resolvers = {
Query: {
user: (_, { id }, context) => {
if (!context.user) throw new AuthenticationError('Login required');
return User.findById(id);
},
},
User: {
email: (user, _, context) => {
if (context.user.id === user.id || context.user.role === 'admin')
return user.email;
return null; // Mask for other users
},
ssn: (user, _, context) => {
if (context.user.role !== 'admin')
throw new ForbiddenError('Insufficient permissions');
return user.ssn;
},
salary: (user, _, context) => {
if (context.user.role !== 'hr' && context.user.id !== user.id)
return null;
return user.salary;
},
},
};
GraphQL Security Checklist
| Control | Tool/Library | Priority |
|---|---|---|
| Disable introspection in production | Apollo config, graphql-disable-introspection | Critical |
| Query depth limiting | graphql-depth-limit | Critical |
| Query cost analysis | graphql-validation-complexity | High |
| Batch limiting | Custom middleware | High |
| Field-level authorization | graphql-shield, custom directives | Critical |
| Persisted queries only | Apollo automatic persisted queries | High |
| Input validation | Custom scalars, joi, zod | High |
| Rate limiting per operation | graphql-rate-limit | Medium |
| Logging & monitoring | Apollo Studio, custom plugins | Medium |
Free Security Review
Worried about your GraphQL API security? We review real code — not just config files. Request a free 20-line code review →
Published by the SecureCodeReviews.com team — helping development teams ship secure GraphQL APIs since 2024.
Advertisement
Free Security Tools
Try our tools now
Expert Services
Get professional help
OWASP Top 10
Learn the top risks
Related Articles
Secure API Design Patterns: A Developer's Guide
Learn the essential security patterns every API developer should implement, from authentication to rate limiting.
JWT Security: Vulnerabilities, Best Practices & Implementation Guide
Comprehensive JWT security guide covering token anatomy, common vulnerabilities, RS256 vs HS256, refresh tokens, and secure implementation patterns.
How to Secure AI Agents: Identity & Access Management for Agentic AI
Machine identities now outnumber human identities 45:1. Learn how to implement IAM for AI agents — authentication, authorization, credential management, and delegation chains in multi-agent systems.