Key Takeaways

Why GraphQL Subscriptions Are a Bug Bounty Gold Mine

GraphQL subscriptions let clients receive real-time updates over a persistent connection. When a user places an order, when a message arrives, when a price changes — subscriptions push that data to the client instantly. The problem: most developers treat subscriptions as a convenience feature and skip the authorization checks they'd never forget on a query or mutation.

This guide covers the practical techniques for finding and exploiting GraphQL subscription vulnerabilities in bug bounty targets. Every technique here has been validated against real-world applications.

How GraphQL Subscriptions Work (Attack-Relevant Details)

Before you test, you need to understand the subscription lifecycle — each phase is an attack surface:

  1. Connection initialization — The client opens a WebSocket and sends a connection_init message, optionally with auth credentials. The server responds with connection_ack. If the server doesn't validate credentials here, every subsequent subscription runs unauthenticated.
  2. Subscription registration — The client sends a subscribe message with a GraphQL subscription query. The server registers the subscription and starts pushing matching events. If the server doesn't check authorization per-subscription, any authenticated user can subscribe to any event stream.
  3. Event delivery — The server pushes next messages as events occur. If field-level authorization isn't enforced on subscription payloads, sensitive fields leak to unauthorized subscribers.
  4. Connection termination — The client sends complete or closes the WebSocket. If the server doesn't clean up subscriptions on disconnect, orphaned subscriptions consume resources indefinitely.

Two Protocols, Two Attack Surfaces

There are two competing WebSocket sub-protocols for GraphQL:

Reconnaissance: Finding Subscription Endpoints

Step 1: Schema Introspection

Run a standard introspection query against the GraphQL endpoint. Look for the subscriptionType field in the schema:

{
  __schema {
    subscriptionType {
      name
      fields {
        name
        args { name type { name } }
        type { name kind ofType { name } }
      }
    }
  }
}

If introspection is disabled, try these bypasses:

Step 2: Identify the WebSocket Endpoint

Common subscription endpoint patterns:

Check the browser's Network tab → WS filter while using the application's real-time features. The WebSocket URL and the Sec-WebSocket-Protocol header tell you everything you need.

Attack 1: Unauthorized Subscription Access (IDOR via Subscriptions)

The most common and highest-impact finding. The server checks authorization on queries and mutations but not on subscriptions.

Testing Methodology

  1. Authenticate as User A. Open a WebSocket and subscribe to User A's events (e.g., subscription { orderUpdated(userId: "A") { id status amount } }).
  2. Confirm you receive events for User A.
  3. Now subscribe to User B's events: subscription { orderUpdated(userId: "B") { id status amount } }.
  4. If you receive User B's events, you have an authorization bypass — report it.

Practical Example with websocat

# Connect with User A's token
websocat -H "Sec-WebSocket-Protocol: graphql-ws" \
  wss://api.target.com/graphql

# Send connection_init with auth
{"type":"connection_init","payload":{"Authorization":"Bearer USER_A_TOKEN"}}

# Subscribe to User B's events
{"id":"1","type":"subscribe","payload":{"query":"subscription { orderUpdated(userId: \"USER_B_ID\") { id status amount shippingAddress } }"}}

Variations to test:

Attack 2: Connection-Level Authentication Bypass

Many GraphQL subscription implementations only check the auth token during connection_init and never re-validate it. This creates two exploitable scenarios:

Scenario A: Token Expiry Bypass

  1. Connect with a valid token.
  2. Wait for the token to expire (or use a token that expires in 1 minute).
  3. Register new subscriptions after expiry.
  4. If the server accepts them, the connection outlives the token — report it.

Scenario B: Revoked Token Persistence

  1. Connect with a valid token.
  2. In another session, revoke the token (logout, password change, admin revocation).
  3. Check if the existing WebSocket connection still delivers subscription events.
  4. If it does, revocation doesn't propagate to active connections — report it.

Attack 3: Subscription Flooding (Denial of Service)

Each active subscription consumes server memory and CPU. Most implementations don't limit the number of subscriptions per connection or per user.

Testing Methodology

# Python script to test subscription limits
import asyncio, websockets, json

async def flood():
    uri = "wss://api.target.com/graphql"
    async with websockets.connect(uri, subprotocols=["graphql-ws"]) as ws:
        await ws.send(json.dumps({
            "type": "connection_init",
            "payload": {"Authorization": "Bearer TOKEN"}
        }))
        await ws.recv()  # connection_ack

        for i in range(1000):
            await ws.send(json.dumps({
                "id": str(i),
                "type": "subscribe",
                "payload": {"query": "subscription { priceUpdated { symbol price } }"}
            }))

        # Monitor server response time degradation
        while True:
            msg = await ws.recv()
            print(msg)

asyncio.run(flood())

Important: Start with small numbers (10, 50, 100) and monitor impact. Do NOT launch thousands of subscriptions against a production target without confirming the program allows DoS testing. Many programs explicitly exclude DoS — check the policy first.

What to Report

Attack 4: Subscription Payload Data Leakage

Even when subscription access is authorized, the payload may include fields the subscriber shouldn't see.

Testing Methodology

  1. Subscribe to an event you're authorized to receive.
  2. Request fields that should be restricted — admin fields, internal IDs, PII of other users, pricing data, etc.
  3. Compare the subscription payload to what the equivalent query returns. If the subscription returns more fields, the authorization layer is inconsistent.
# Subscribe requesting sensitive fields
{"id":"1","type":"subscribe","payload":{"query":"subscription { orderUpdated { id status amount internalNotes adminComments customerEmail customerPhone }"}}

This works because many implementations use a different resolver chain for subscriptions than for queries. The query resolver checks field-level permissions; the subscription resolver just serializes the entire event object.

Attack 5: Subscription Injection

If subscription arguments are interpolated into backend event filters without sanitization, you can inject filter logic:

Tools for GraphQL Subscription Testing

ToolUse Case
Burp SuiteWebSocket interception, message modification, replay
websocatCommand-line WebSocket client for manual testing
InQLBurp extension for GraphQL introspection and query generation
GraphQL VoyagerVisual schema exploration to identify subscription types
Python websocketsScripted subscription testing and fuzzing

Reporting GraphQL Subscription Vulnerabilities

Severity Guidelines

Report Template

## Title
Unauthorized Cross-Tenant Data Access via GraphQL Subscriptions

## Severity
Critical

## Description
The GraphQL subscription endpoint at wss://api.target.com/graphql
does not enforce per-user authorization on subscription registration.
An authenticated user can subscribe to events belonging to any other
user by specifying their userId in the subscription query.

## Steps to Reproduce
1. Authenticate as User A (attacker)
2. Open WebSocket to wss://api.target.com/graphql
3. Send connection_init with User A's bearer token
4. Send subscribe message with User B's userId
5. Trigger an event for User B (e.g., place an order)
6. Observe User B's event data delivered to User A's subscription

## Impact
Any authenticated user can monitor real-time events for any other
user, including order details, messages, and account changes.

Defensive Patterns (Know What You're Bypassing)

Understanding the defenses helps you find where they're missing:

Related Guides

Advertisement