Skip to content

GraphQL Security Testing

Peter Mueller edited this page Mar 26, 2026 · 1 revision

GraphQL Security Testing with Hadrian

Hadrian provides 13 built-in GraphQL security templates covering introspection disclosure, denial-of-service attacks, authorization bypass, injection, and data exposure vulnerabilities.

Quick Start

# Basic GraphQL security scan (uses introspection to discover schema)
hadrian test graphql --target https://api.example.com --template-dir templates/graphql

# With SDL schema file (when introspection is disabled)
hadrian test graphql --target https://api.example.com --schema schema.graphql --template-dir templates/graphql

# With authentication for authorization testing
hadrian test graphql --target https://api.example.com --auth auth.yaml --roles roles.yaml --template-dir templates/graphql

Command Options

hadrian test graphql [flags]

Flags:
      --target string          Target base URL (required)
      --endpoint string        GraphQL endpoint path (default "/graphql")
      --schema string          GraphQL SDL schema file (optional — uses introspection if not provided)
      --roles string           Roles and permissions YAML file
      --auth string            Authentication configuration YAML file
      --template-dir string    GraphQL templates directory
      --depth-limit int        Maximum query depth for DoS testing (default 10)
      --complexity-limit int   Maximum complexity for DoS testing (default 1000)
      --batch-size int         Queries in batch attack tests (default 100)
      --proxy string           HTTP/HTTPS proxy URL
      --insecure               Skip TLS verification
      --rate-limit float       Requests per second (default 5.0)
      --timeout int            Request timeout in seconds (default 30)
      --output string          Output format: terminal, json, markdown (default "terminal")
      --output-file string     Output file path
      --verbose                Verbose output
      --dry-run                Show what would be tested without executing

Built-In Security Templates

Template OWASP Category Vulnerability Severity
introspection-disclosure API8:2023 Introspection enabled in production MEDIUM
depth-attack API4:2023 Missing query depth limits HIGH
batching-attack API4:2023 Missing batch query limits MEDIUM
alias-dos-attack API4:2023 Alias-based denial of service HIGH
field-duplication-attack API4:2023 Field duplication DoS MEDIUM
circular-fragment-attack API4:2023 Circular fragment DoS HIGH
directive-overloading API4:2023 Directive abuse MEDIUM
bola-user-access API1:2023 Broken object-level authorization HIGH
bfla-mutation API5:2023 Broken function-level authorization HIGH
injection-via-variables API8:2023 GraphQL injection HIGH
error-disclosure API8:2023 Verbose error messages MEDIUM
excessive-data-exposure API3:2023 Over-fetching sensitive data HIGH

What Each Check Detects

Introspection Disclosure — Detects if GraphQL introspection is enabled in production, allowing attackers to discover the entire API schema.

Query Depth/Batching/Alias Attacks — Tests for missing rate limits and complexity controls that could enable resource exhaustion denial-of-service attacks.

BOLA (API1:2023) — Tests if users can access other users' data through object ID manipulation in GraphQL queries.

BFLA (API5:2023) — Tests if low-privileged users can execute admin-only mutations.

Injection via Variables — Tests for GraphQL injection through query variables.

Error Disclosure — Detects verbose error messages that leak internal implementation details.

Excessive Data Exposure — Tests if queries return more data fields than the user should have access to.

Authentication for Authorization Testing

BOLA and BFLA tests require authentication configuration. See Configuration for all auth methods.

# auth.yaml
roles:
  admin:
    type: bearer
    token: "admin-jwt-token..."
  user:
    type: bearer
    token: "user-jwt-token..."
  attacker:
    type: bearer
    token: "attacker-jwt-token..."
# roles.yaml
roles:
  - name: admin
    permissions:
      - "read:*:all"
      - "write:*:all"
      - "delete:*:all"
  - name: user
    permissions:
      - "read:user:own"
      - "write:user:own"
  - name: attacker
    permissions:
      - "read:user:own"

Writing Custom GraphQL Templates

id: graphql-custom-check
info:
  name: "Custom GraphQL Check"
  category: "API3:2023"
  severity: "HIGH"
  test_pattern: "simple"

endpoint_selector:
  requires_auth: true

role_selector:
  attacker_permission_level: "lower"
  victim_permission_level: "higher"

graphql:
  - query: |
      query {
        sensitiveData {
          secret
        }
      }
    auth: "attacker"
    matchers:
      - type: status
        status: [200]
      - type: word
        words: ["secret"]
        part: body

detection:
  success_indicators:
    - type: status_code
      status_code: 200
    - type: body_field
      body_field: "data.sensitiveData"
      exists: true

See the Template System guide for the full template format.

Tutorial: Testing DVGA

DVGA (Damn Vulnerable GraphQL Application) is an intentionally vulnerable GraphQL app for testing.

Setup DVGA

git clone https://github.com/dolevf/Damn-Vulnerable-GraphQL-Application.git
cd Damn-Vulnerable-GraphQL-Application
docker-compose up -d
# DVGA runs at http://localhost:5013/graphql

Run Hadrian Against DVGA

hadrian test graphql --target http://localhost:5013 --verbose

# Expected findings:
# [MEDIUM] introspection-disclosure: GraphQL introspection is enabled
# [HIGH]   depth-attack: Server allows deeply nested queries (depth 10)
# [MEDIUM] batching-attack: Server allows batched queries (100 operations)

For the complete tutorial, see Tutorials — DVGA.

Troubleshooting

"Introspection is disabled"

Provide a schema file when introspection is disabled:

hadrian test graphql --target https://api.example.com --schema schema.graphql

"TLS certificate error"

For self-signed certificates or proxy interception:

hadrian test graphql --target https://api.example.com --insecure
# Or with a CA certificate
hadrian test graphql --target https://api.example.com --ca-cert ca.crt

Best Practices

  1. Start with --dry-run to preview what will be tested
  2. Use appropriate rate limits — don't overwhelm the target server
  3. Test in staging first — never test production without explicit authorization
  4. Provide auth for BOLA/BFLA — authorization tests require valid tokens for multiple roles
  5. Enable LLM triage with requires_llm_triage: true for better finding context

Clone this wiki locally