graphql-security/SKILL.md
Use when building or auditing GraphQL APIs — comprehensive security hardening covering introspection exposure, DoS attacks (circular queries, alias overloading, batching), injection, authorization bypass, CSRF, SSRF, WebSocket hijacking, and the...
npx skillsauth add peterbamuhigire/skills-web-dev graphql-securityInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
graphql-security or would be better handled by a more specific companion skill.SKILL.md first, then load only the referenced deep-dive files that are necessary for the task.GraphQL's single-endpoint design and client-controlled query shape create a fundamentally different attack surface than REST. Standard WAFs, request-count rate limiting, and REST security checklists are all insufficient. Load this skill alongside api-design-first for every GraphQL build.
Cardinal rule: Authentication at the gateway. Authorization at the business logic layer (between resolvers and DB). Never in resolvers alone — resolver-level auth is missed on alternate query paths.
Single endpoint /graphql
├── Queries → IDOR, info disclosure, recursive DoS
├── Mutations → injection, CSRF, privilege escalation
├── Subscriptions → WebSocket hijacking (CSWSH)
├── Arguments → SQLi, OS injection, ReDoS, SSRF
├── Aliases → brute-force bypass, alias overloading DoS
├── Fragments → circular fragment DoS
├── Directives → directive overloading DoS
└── Introspection → full schema exposure
Detect GraphQL: Send { __typename } — returns {"data":{"__typename":"Query"}} on any implementation.
Risk: All types, fields, mutations, arguments, deprecated fields — full API blueprint — returned in a single request.
Bypass when "disabled": Many servers block __schema but not __type. Test: { __type(name:"Query") { name } } — if data returns, introspection is only partially blocked. Also test via WebSocket if HTTP is blocked.
Defense:
introspection: false in server config)__schema, __type, __typename (if not needed), __field, __inputvalueRisk: "Did you mean X?" hints allow full schema reconstruction without introspection. Tools like Clairvoyance automate this with a 30,000-word wordlist.
Defense:
graphql.pyutils.did_you_mean.MAX_LENGTH = 0Risk: Cross-referencing types (A → B → A) allow exponential server load. One deeply nested query can crash servers. Circular fragments (fragment A { ...B } + fragment B { ...A }) crash non-spec-compliant implementations.
Real-world: GitLab (2019) — circular introspection query DoS. CVE-2022-30288 (Agoo Ruby).
Defense:
# Graphene (Python) — depth and cost limits
from graphene import ObjectType
from graphql import build_schema
from graphql.validation import NoCircularFragmentsRule
# graphql-ruby
max_depth 10
# graphene-django
GRAPHENE = { "MIDDLEWARE": ["graphql_auth.middleware.JWTMiddleware"] }
depth_limit_validator(max_depth=20)
max_depth 10–20 levels (lower for public APIs)MAX_COSTRisk: A single HTTP request with 500 aliased mutations = 500 operations counted as 1 for rate limiting. Field duplication ({ content content content } × 1000) causes CPU exhaustion — server resolves each copy but returns only one.
Real-world: Magento (April 2021) — unauthenticated alias-based DoS.
Defense:
Risk: Some servers accept [{"query":"..."}, {"query":"..."}, ...] — an array of queries in one HTTP request. Sending 10,000 queries in one array bypasses all request-count rate limiting.
Real-world: WPGraphQL (April 2021) — batching enabled by default with no controls.
Defense:
batch=FalseGraphQL Cop to audit: python3 graphql-cop.py -t http://target/graphqlRisk: Stuffing thousands of non-existent directives into a query (@a@b@c@d@e...) exhausts the query parser — can crash servers before any resolver executes.
Defense:
client_max_body_size)Risk: Alias batching sends hundreds of login attempts as one HTTP request, bypassing per-request rate limits.
mutation {
a1: login(username:"admin", password:"password1") { token }
a2: login(username:"admin", password:"password2") { token }
# ... 500 more
}
Tool: CrackQL automates alias-based brute force using CSV wordlists.
Defense:
Risk: GraphQL often exposes multiple paths to the same object type. Authorization on pastes doesn't protect readAndBurn if it returns the same PasteObject. Attackers use graphql-path-enum to find unprotected paths.
Real-world: GitLab (private notes via GraphQL bypassed REST restrictions, CVE-2019-15576). Shopify (staff modifying customer emails, HackerOne #980511).
Defense:
graphql-path-enum -i introspection.json -t TargetTypefallbackRule to deny (default is allow — a trap)Risk: GraphQL type checking only validates scalar type (String/Int), not content. String arguments passed into SQL without parameterization are injectable.
Real-world: Apache SkyWalking (CVE-2020-9483) — getLinearIntValues(metric:{id:"..."}).
Defense:
Risk: Resolvers passing arguments to shell functions are exploitable. High-risk field names: system, debug, diagnostics, execute, run, update.
Defense:
psutil not ps)Risk: Servers accepting mutations via POST with application/x-www-form-urlencoded or via GET are forgeable from any HTML page. Victim visits attacker page → form auto-submits → mutation executes as victim.
Real-world: GitLab (March 2021, HackerOne #1122408) — GET-based mutations bypassed X-CSRF-Token.
Defense:
Content-Type: application/json on all GraphQL requests — reject application/x-www-form-urlencoded and text/plainSameSite=Strict on session cookiesRisk: JavaScript can open WebSocket connections to any origin without CORS restrictions. If the server doesn't validate the Origin header on the WebSocket handshake, attacker pages can subscribe to real-time events using the victim's session cookies.
Defense:
Origin header on every WebSocket handshake using exact string match (substring matching is bypassable: example.com.attacker.net contains example.com)SameSite=Strict cookiesRisk: Mutations/queries that accept URL arguments may be redirected to internal services, private IP ranges, or cloud metadata endpoints (169.254.169.254 → AWS IAM credentials).
High-risk argument names: url, host, ip, domain, site, fetch, remote_url, target
Defense:
10.x.x.x, 172.16.x.x, 192.168.x.x) in URL argumentsRisk: Mutation accepting HTML/JS content stores it; when other users view the data it executes in their browser.
Defense:
Content-Type)Standard request-count limits are insufficient for GraphQL. Layer all of these:
| Control | Implementation | Limit |
|---|---|---|
| Query depth | max_depth server config | 10–20 levels |
| Query cost | Assign cost per field, enforce MAX_COST | Set per app |
| Aliases | Custom middleware: count aliases per document | ≤ 10 |
| Array batch size | Limit queries per batch array | ≤ 10 |
| Execution timeout | Server + Nginx timeout | 20–60s |
| Body size | Nginx client_max_body_size | 100KB–1MB |
| Auth operations | Per-user attempt count across all aliases | 5/min |
Strongest defense: Automatic Persisted Queries (APQ) — clients register query hashes; server only executes pre-approved queries. Unknown queries are rejected. Supported by Apollo GraphQL.
extensions.tracing (reveals per-resolver timing — useful to attackers)/graphiql, /playground) in production/graphql but forget /api/graphql or /graphql/v2Referer headers/graphiql, /playground) removed or restricted in productionMAX_COST thresholdalg: "none" rejectedgraphql-path-enum)fallbackRule set to denyContent-Type: application/json enforced; URL-encoded POST rejectedSameSite=Strict or Lax on session cookiesOrigin header validated on WebSocket handshake (exact match)extensions.tracing disabled in production| Tool | Purpose |
|---|---|
| GraphQL Cop | Automated audit (DoS, info disclosure, CSRF) |
| InQL | Schema extraction, query templates, circular query detection |
| Clairvoyance | Schema reconstruction via field suggestions |
| CrackQL | Alias-based brute force (passwords, IDOR, 2FA) |
| BatchQL | Detect array batching and CSRF exposure |
| Graphw00f | Fingerprint GraphQL implementation |
| graphql-path-enum | Enumerate all paths to a target object type |
| Burp Suite + GraphQL Raider | Intercept/replay GraphQL traffic |
| SQLmap | Automated SQL injection testing (-r request.txt --dbms=sqlite) |
| DVGA | docker run -p 5013:5013 dvga — local practice target |
Aleks, N. & Farhi, D. — Black Hat GraphQL (No Starch Press, 2023); Johnson, P. — Modern API Design (2024) Ch.7–8; Gurbani, N. — Mastering RESTful API Development with Go (2024)
data-ai
Use when adding AI-powered analytics to a SaaS platform — semantic search over business data, natural language queries, trend detection, anomaly alerts, and AI-generated insights for dashboards. Covers embeddings, NL2SQL, and per-tenant analytics...
data-ai
Design AI-powered analytics dashboards — what metrics to show, how to display AI predictions and confidence, drill-down patterns, KPI cards, trend visualisation, AI Insights panels, export design, and role-based dashboard variants. Invoke when...
development
Use when designing, building, reviewing, or upgrading production software systems that must be secure, performant, maintainable, scalable, and user-centered. Apply before writing specs, code, architecture, APIs, databases, mobile apps, SaaS platforms, or ERP systems.
development
Professional web app UI using commercial templates (Tabler/Bootstrap 5) with strong frontend design direction when needed. Use for CRUD interfaces, dashboards, admin panels with SweetAlert2, DataTables, Flatpickr. Clone seeder-page.php, use...