<graphql_protocol_guide>
<title>GRAPHQL — ADVANCED TESTING AND EXPLOITATION</title>

<critical>GraphQL’s flexibility enables powerful data access, but also unique failures: field- and edge-level authorization drift, schema exposure (even with introspection off), alias/batch abuse, resolver injection, federated trust gaps, and complexity/fragment bombs. Bind subject→action→object at resolver boundaries and validate across every transport and feature flag.</critical>

<scope>
- Queries, mutations, subscriptions (graphql-ws, graphql-transport-ws)
- Persisted queries/Automatic Persisted Queries (APQ)
- Federation (Apollo/GraphQL Mesh): _service SDL and _entities
- File uploads (GraphQL multipart request spec)
- Relay conventions: global node IDs, connections/cursors
</scope>

<methodology>
1. Fingerprint endpoint(s), transport(s), and stack (framework, plugins, gateway). Note GraphiQL/Playground exposure and CORS/credentials.
2. Obtain multiple principals (unauth, basic, premium, admin/staff) and capture at least one valid object ID per subject.
3. Acquire schema via introspection; if disabled, infer iteratively from errors, field suggestions, __typename probes, vocabulary brute-force.
4. Build an Actor × Operation × Type/Field matrix. Exercise each resolver path with swapped IDs, roles, tenants, and channels (REST proxies, GraphQL HTTP, WS).
5. Validate consistency: same authorization and validation across queries, mutations, subscriptions, batch/alias, persisted queries, and federation.
</methodology>

<discovery_techniques>
<endpoint_finding>
- Common paths: /graphql, /api/graphql, /v1/graphql, /gql
- Probe with minimal canary:
{% raw %}
POST /graphql {"query":"{__typename}"}
GET  /graphql?query={__typename}
{% endraw %}
- Detect GraphiQL/Playground; note if accessible cross-origin and with credentials.
</endpoint_finding>

<introspection_and_inference>
- If enabled, dump full schema; otherwise:
  - Use __typename on candidate fields to confirm types
  - Abuse field suggestions and error shapes to enumerate names/args
  - Infer enums from “expected one of” errors; coerce types by providing wrong shapes
  - Reconstruct edges from pagination and connection hints (pageInfo, edges/node)
</introspection_and_inference>

<schema_construction>
- Map root operations, object types, interfaces/unions, directives (@auth, @defer, @stream), and custom scalars (Upload, JSON, DateTime)
- Identify sensitive fields: email, tokens, roles, billing, file keys, admin flags
- Note cascade paths where child resolvers may skip auth under parent assumptions
</schema_construction>
</discovery_techniques>

<exploitation_techniques>
<authorization_and_idor>
- Test field-level and edge-level checks, not just top-level gates. Pair owned vs foreign IDs within the same request via aliases to diff responses.
{% raw %}
query {
  me { id }
  a: order(id:"A_OWNER") { id total owner { id email } }
  b: order(id:"B_FOREIGN") { id total owner { id email } }
}
{% endraw %}
- Probe mutations for partial updates that bypass validation (JSON Merge Patch semantics in inputs).
- Validate node/global ID resolvers (Relay) bind to the caller; decode/replace base64 IDs and compare access.
</authorization_and_idor>

<batching_and_alias>
- Alias to perform many logically separate reads in one operation; watch for per-request vs per-field auth discrepancies
- If array batching is supported (non-standard), submit multiple operations to bypass rate limits and achieve partial failures
{% raw %}
query {
  u1:user(id:"1"){email}
  u2:user(id:"2"){email}
  u3:user(id:"3"){email}
}
{% endraw %}
</batching_and_alias>

<variable_and_shape_abuse>
- Scalars vs objects vs arrays: {% raw %}{id:123}{% endraw} vs {% raw %}{id:"123"}{% endraw} vs {% raw %}{id:[123]}{% endraw}; send null/empty/0/-1 and extra object keys retained by backend
- Duplicate keys in JSON variables: {% raw %}{"id":1,"id":2}{% endraw} (parser precedence), default argument values, coercion errors leaking field names
</variable_and_shape_abuse>

<cursor_and_projection>
- Decode cursors (often base64) to manipulate offsets/IDs and skip filters
- Abuse selection sets and fragments to force overfetching of sensitive subfields
</cursor_and_projection>

<file_uploads>
- GraphQL multipart: test multiple Upload scalars, filename/path tricks, unexpected content-types, oversize chunks; verify server-side ownership/scoping for returned URLs
</file_uploads>
</exploitation_techniques>

<advanced_techniques>
<introspection_bypass>
- Field suggestion leakage: submit near-miss names to harvest suggestions
- Error taxonomy: different codes/messages for unknown field vs unauthorized field reveal existence
- __typename sprinkling on edges to confirm types without schema
</introspection_bypass>

<defer_and_stream>
- Use @defer and @stream to obtain partial results or subtrees hidden by parent checks; confirm server supports incremental delivery
{% raw %}
query @defer {
  me { id }
  ... @defer { adminPanel { secrets } }
}
{% endraw %}
</defer_and_stream>

<fragment_and_complexity_bombs>
- Recursive fragment spreads and wide selection sets cause CPU/memory spikes; craft minimal reproducible bombs to validate cost limits
{% raw %}
fragment x on User { friends { ...x } }
query { me { ...x } }
{% endraw %}
- Validate depth/complexity limiting, query cost analyzers, and timeouts
</fragment_and_complexity_bombs>

<federation>
- Apollo Federation: query _service { sdl } if exposed; target _entities to materialize foreign objects by key without proper auth in subgraphs
{% raw %}
query {
  _entities(representations:[
    {__typename:"User", id:"TARGET"}
  ]) { ... on User { email roles } }
}
{% endraw %}
- Look for auth done at gateway but skipped in subgraph resolvers; cross-subgraph IDOR via inconsistent ownership checks
</federation>

<subscriptions>
- Check message-level authorization, not only handshake; attempt to subscribe to channels for other users/tenants; test cross-tenant event leakage
- Abuse filter args in subscription resolvers to reference foreign IDs
</subscriptions>

<persisted_queries>
- APQ hashes can be guessed/bruteforced or leaked from clients; replay privileged operations by supplying known hashes with attacker variables
- Validate that hash→operation mapping enforces principal and operation allowlists
</persisted_queries>

<csrf_and_cors>
- If cookie-auth is used and GET is accepted, test CSRF on mutations via query parameters; verify SameSite and origin checks
- Cross-origin GraphiQL/Playground exposure with credentials can leak data via postMessage bridges
</csrf_and_cors>

<waf_evasion>
- Reshape queries: comments, block strings, Unicode escapes, alias/fragment indirection, JSON variables vs inline args, GET vs POST vs application/graphql
- Split fields across fragments and inline spreads to avoid naive signatures
</waf_evasion>
</advanced_techniques>

<bypass_techniques>
<transport_and_parsers>
- Toggle content-types: application/json, application/graphql, multipart/form-data; try GET with query and variables params
- HTTP/2 multiplexing and connection reuse to widen timing windows and rate limits
</transport_and_parsers>

<naming_and_aliasing>
- Case/underscore variations, Unicode homoglyphs (server-dependent), aliases masking sensitive field names
</naming_and_aliasing>

<gateway_and_cache>
- CDN/key confusion: responses cached without considering Authorization or variables; manipulate Vary and Accept headers
- Redirects and 304/206 behaviors leaking partially cached GraphQL responses
</gateway_and_cache>
</bypass_techniques>

<special_contexts>
<relay>
- node(id:…) global resolution: decode base64, swap type/id pairs, ensure per-type authorization is enforced inside resolvers
- Connections: verify that filters (owner/tenant) apply before pagination; cursor tampering should not cross ownership boundaries
</relay>

<server_plugins>
- Custom directives (@auth, @private) and plugins often annotate intent but do not enforce; verify actual checks in each resolver path
</server_plugins>
</special_contexts>

<chaining_attacks>
- GraphQL + IDOR: enumerate IDs via list fields, then fetch or mutate foreign objects
- GraphQL + CSRF: trigger mutations cross-origin when cookies/auth are accepted without proper checks
- GraphQL + SSRF: resolvers that fetch URLs (webhooks, metadata) abused to reach internal services
</chaining_attacks>

<validation>
1. Provide paired requests (owner vs non-owner) differing only in identifiers/roles that demonstrate unauthorized access or mutation.
2. Prove resolver-level bypass: show top-level checks present but child field/edge exposes data.
3. Demonstrate transport parity: reproduce via HTTP and WS (subscriptions) or via persisted queries.
4. Minimize payloads; document exact selection sets and variable shapes used.
</validation>

<false_positives>
- Introspection available only on non-production/stub endpoints
- Public fields by design with documented scopes
- Aggregations or counts without sensitive attributes
- Properly enforced depth/complexity and per-resolver authorization across transports
</false_positives>

<impact>
- Cross-account/tenant data exposure and unauthorized state changes
- Bypass of federation boundaries enabling lateral access across services
- Credential/session leakage via lax CORS/CSRF around GraphiQL/Playground
</impact>

<pro_tips>
1. Always diff the same operation under multiple principals with aliases in one request.
2. Sprinkle __typename to map types quickly when schema is hidden.
3. Attack edges: child resolvers often skip auth compared to parents.
4. Try @defer/@stream and subscriptions to slip gated data in incremental events.
5. Decode cursors and node IDs; assume base64 unless proven otherwise.
6. Federation: exercise _entities with crafted representations; subgraphs frequently trust gateway auth.
7. Persisted queries: extract hashes from clients; replay with attacker variables.
8. Keep payloads small and structured; restructure rather than enlarge to evade WAFs.
9. Validate defenses by code/config review where possible; don’t trust directives alone.
10. Prove impact with role-separated, transport-separated, minimal PoCs.
</pro_tips>

<remember>GraphQL security is resolver security. If any resolver on the path to a field fails to bind subject, object, and action, the graph leaks. Validate every path, every transport, every environment.</remember>
</graphql_protocol_guide>
