Graphql Security Specialist Companion
Battle-tested agent for graphql, security, authorization, specialist. Includes structured workflows, validation checks, and reusable patterns for api graphql.
GraphQL Security Specialist Companion
An autonomous agent that hardens GraphQL APIs against common attack vectors ā implementing query depth limiting, introspection controls, injection prevention, and rate limiting to prevent abuse and data exfiltration.
When to Use This Agent
Choose GraphQL Security Specialist Companion when:
- You are deploying a GraphQL API to production and need security hardening
- You want to prevent query complexity attacks, introspection abuse, or injection
- You need to implement authentication and field-level authorization
- Security audits have flagged GraphQL-specific vulnerabilities
Consider alternatives when:
- You need general web application security (use a security auditor agent)
- Your API is REST, not GraphQL (GraphQL-specific patterns do not apply)
- You need infrastructure/network security (use a DevSecOps agent)
Quick Start
# .claude/agents/graphql-security.yml name: graphql-security-specialist-companion description: Harden GraphQL APIs against attacks agent_prompt: | You are a GraphQL Security Specialist. Secure GraphQL APIs by: 1. Disabling introspection in production 2. Implementing query depth and complexity limits 3. Setting up field-level authorization 4. Preventing injection through variable validation 5. Rate limiting by query complexity, not just request count 6. Implementing persisted queries to prevent arbitrary queries Security is non-negotiable. Default to restrictive and open up as needed.
Example invocation:
claude "Harden our GraphQL API for production deployment ā currently has no security controls"
Sample security audit output:
GraphQL Security Audit
āāāāāāāāāāāāāāāāāāāāāā
Risk Level: CRITICAL ā 6 vulnerabilities found
[CRITICAL] Introspection enabled in production
Risk: Attackers can discover entire schema
Fix: Disable introspection in production env
[CRITICAL] No query depth limit
Risk: Recursive queries exhaust server resources
Fix: Set max depth to 7
[HIGH] No field-level authorization
Risk: User A can query User B's private data
Fix: Add @auth directives on sensitive fields
[HIGH] No query complexity limit
Risk: Single query can trigger millions of DB rows
Fix: Implement cost analysis with max complexity 1000
[MEDIUM] No rate limiting
Risk: Brute-force and enumeration attacks
Fix: Rate limit by IP + user + query complexity
[LOW] Error messages expose stack traces
Risk: Information disclosure
Fix: Sanitize errors in production
Core Concepts
GraphQL Attack Surface
| Attack | Description | Mitigation |
|---|---|---|
| Depth attack | Deeply nested recursive queries | Query depth limit (max 7) |
| Complexity attack | Wide queries loading millions of items | Complexity analysis + limits |
| Introspection abuse | Schema discovery for targeted attacks | Disable in production |
| Injection | Malicious values in variables | Input validation + parameterization |
| Batching attack | Thousands of queries in one request | Batch size limit |
| Authorization bypass | Accessing fields without permission | Field-level auth directives |
Security Middleware Stack
import depthLimit from 'graphql-depth-limit'; import { createComplexityLimitRule } from 'graphql-validation-complexity'; const server = new ApolloServer({ schema, validationRules: [ depthLimit(7), createComplexityLimitRule(1000), ], introspection: process.env.NODE_ENV !== 'production', plugins: [ // Rate limiting by complexity { requestDidStart() { return { didResolveOperation({ request, document }) { const complexity = calculateComplexity(document); const rateLimitKey = `${request.http?.headers.get('x-user-id')}`; if (isRateLimited(rateLimitKey, complexity)) { throw new Error('Rate limit exceeded'); } } }; } }, // Error sanitization { requestDidStart() { return { willSendResponse({ response }) { if (process.env.NODE_ENV === 'production') { sanitizeErrors(response); } } }; } } ], formatError: (error) => { if (process.env.NODE_ENV === 'production') { return { message: error.message, code: error.extensions?.code }; } return error; } });
Field-Level Authorization
// Schema directive for authorization const typeDefs = gql` directive @auth(requires: Role!) on FIELD_DEFINITION enum Role { USER ADMIN OWNER } type User { id: ID! name: String! email: String! @auth(requires: OWNER) phone: String @auth(requires: OWNER) role: Role! @auth(requires: ADMIN) posts: [Post!]! } `; // Directive implementation class AuthDirective extends SchemaDirectiveVisitor { visitFieldDefinition(field) { const requiredRole = this.args.requires; const originalResolve = field.resolve; field.resolve = async function(source, args, context, info) { if (!context.user) throw new AuthenticationError('Not authenticated'); const isOwner = source.id === context.user.id; const isAdmin = context.user.role === 'ADMIN'; if (requiredRole === 'OWNER' && !isOwner && !isAdmin) { return null; // Silently hide unauthorized fields } if (requiredRole === 'ADMIN' && !isAdmin) { throw new ForbiddenError('Admin access required'); } return originalResolve.call(this, source, args, context, info); }; } }
Configuration
| Option | Type | Default | Description |
|---|---|---|---|
maxDepth | number | 7 | Maximum query nesting depth |
maxComplexity | number | 1000 | Maximum query cost score |
maxBatchSize | number | 10 | Maximum queries per batch request |
introspection | boolean | false | Enable schema introspection (prod) |
rateLimit | object | { rpm: 100 } | Rate limit configuration |
persistedQueriesOnly | boolean | false | Reject non-persisted queries |
Best Practices
-
Disable introspection in production ā Introspection lets anyone discover your entire schema, including sensitive fields and hidden queries. Disable it in production and use persisted queries so only your own clients can execute queries. Keep introspection enabled in development for tooling.
-
Rate limit by query complexity, not just request count ā A single complex query (loading 10,000 items) costs 100x more than a simple query (loading one item). Assign a complexity cost to each query and rate limit based on total complexity consumed per minute, not just the number of HTTP requests.
-
Never trust client-provided variables ā Validate all input variables with strict schemas. A variable expected to be a positive integer should be validated as such before it reaches your resolvers. Use Zod or Joi validation on all mutation inputs to prevent injection and type confusion attacks.
-
Implement allow-listing (persisted queries) for production ā Instead of allowing any GraphQL query string, pre-register the queries your application uses and only allow those. This prevents attackers from crafting malicious queries, eliminates query parsing overhead, and enables CDN caching.
-
Sanitize error messages in production ā A stack trace in an error response reveals file paths, library versions, and database structure. In production, return only the error message and a machine-readable error code. Log the full error server-side for debugging.
Common Issues
Recursive type definitions enable infinite depth attacks ā A User type with a friends field that returns [User!]! enables queries like { user { friends { friends { friends { ... } } } } }. Depth limiting mitigates this, but also consider removing directly recursive relationships and requiring explicit queries for each level.
Persisted queries break during development ā Developers cannot iterate on queries when only pre-registered queries are allowed. Use persisted queries only in production. In development and staging, allow arbitrary queries with introspection enabled. Automatically register new queries during build time using tools like Apollo's persistedQueryManifest.
Field-level authorization returns inconsistent null values ā When unauthorized fields return null, clients cannot distinguish between "this field is null" and "you do not have permission." Use a consistent pattern: return null for unauthorized fields on other users' data (privacy protection) but throw an error for operations the user should know they cannot perform (admin-only mutations).
Reviews
No reviews yet. Be the first to review this template!
Similar Templates
API Endpoint Builder
Agent that scaffolds complete REST API endpoints with controller, service, route, types, and tests. Supports Express, Fastify, and NestJS.
Documentation Auto-Generator
Agent that reads your codebase and generates comprehensive documentation including API docs, architecture guides, and setup instructions.
Ai Ethics Advisor Partner
All-in-one agent covering ethics, responsible, development, specialist. Includes structured workflows, validation checks, and reusable patterns for ai specialists.