G

Graphql Security Specialist Companion

Battle-tested agent for graphql, security, authorization, specialist. Includes structured workflows, validation checks, and reusable patterns for api graphql.

AgentClipticsapi graphqlv1.0.0MIT
0 views0 copies

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

AttackDescriptionMitigation
Depth attackDeeply nested recursive queriesQuery depth limit (max 7)
Complexity attackWide queries loading millions of itemsComplexity analysis + limits
Introspection abuseSchema discovery for targeted attacksDisable in production
InjectionMalicious values in variablesInput validation + parameterization
Batching attackThousands of queries in one requestBatch size limit
Authorization bypassAccessing fields without permissionField-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

OptionTypeDefaultDescription
maxDepthnumber7Maximum query nesting depth
maxComplexitynumber1000Maximum query cost score
maxBatchSizenumber10Maximum queries per batch request
introspectionbooleanfalseEnable schema introspection (prod)
rateLimitobject{ rpm: 100 }Rate limit configuration
persistedQueriesOnlybooleanfalseReject non-persisted queries

Best Practices

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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).

Community

Reviews

Write a review

No reviews yet. Be the first to review this template!

Similar Templates