7.8 KiB
name, description
| name | description |
|---|---|
| graphql | GraphQL security testing covering introspection, resolver injection, batching attacks, and authorization bypass |
GraphQL
Security testing for GraphQL APIs. Focus on resolver-level authorization, field/edge access control, batching abuse, and federation trust boundaries.
Attack Surface
Operations
- Queries, mutations, subscriptions
- Persisted queries / Automatic Persisted Queries (APQ)
Transports
- HTTP POST/GET with
application/jsonorapplication/graphql - WebSocket: graphql-ws, graphql-transport-ws protocols
- Multipart for file uploads
Schema Features
- Introspection (
__schema,__type) - Directives:
@defer,@stream, custom auth directives (@auth, @private) - Custom scalars: Upload, JSON, DateTime
- Relay: global node IDs, connections/cursors, interfaces/unions
Architecture
- Federation (Apollo, GraphQL Mesh):
_service,_entities - Gateway vs subgraph authorization boundaries
Reconnaissance
Endpoint Discovery
POST /graphql {"query":"{__typename}"}
POST /api/graphql {"query":"{__typename}"}
POST /v1/graphql {"query":"{__typename}"}
POST /gql {"query":"{__typename}"}
GET /graphql?query={__typename}
Check for GraphiQL/Playground exposure with credentials enabled (cross-origin with cookies can leak data via postMessage bridges).
Schema Acquisition
If introspection enabled:
{__schema{types{name fields{name args{name}}}}}
If disabled, infer schema via:
__typenameprobes on candidate fields- Field suggestion errors (submit near-miss names to harvest suggestions)
- "Expected one of" errors revealing enum values
- Type coercion errors exposing field structure
- Error taxonomy: different codes for "unknown field" vs "unauthorized field" reveal existence
Schema Mapping
Map: root operations, object types, interfaces/unions, directives, custom scalars. Identify sensitive fields: email, tokens, roles, billing, API keys, admin flags, file URLs. Note cascade paths where child resolvers may skip auth under parent assumptions.
Key Vulnerabilities
Authorization Bypass
Field-Level IDOR
Test with aliases comparing owned vs foreign objects in single request:
query {
own: order(id:"OWNED_ID") { id total owner { email } }
foreign: order(id:"FOREIGN_ID") { id total owner { email } }
}
Edge/Child Resolver Gaps
Parent resolver checks auth, child resolver assumes it's already validated:
query {
user(id:"FOREIGN") {
id
privateData { secrets } # Child may skip auth check
}
}
Relay Node Resolution
Decode base64 global IDs, swap type/id pairs:
query {
node(id:"VXNlcjoxMjM=") { ... on User { email } }
}
Ensure per-type authorization is enforced inside resolvers. Verify connection filters (owner/tenant) apply before pagination; cursor tampering should not cross ownership boundaries.
Mutation Bypass
- Probe mutations for partial updates bypassing validation (JSON Merge Patch semantics)
- Test mutations that accept extra fields passed to downstream logic
Batching & Alias Abuse
Enumeration via Aliases
query {
u1:user(id:"1"){email}
u2:user(id:"2"){email}
u3:user(id:"3"){email}
}
Bypasses per-request rate limits; exposes per-field vs per-request auth inconsistencies.
Array Batching
If supported (non-standard), submit multiple operations to achieve partial failures and bypass limits.
Input Manipulation
Type Confusion
{id: 123} vs {id: "123"}
{id: [123]} vs {id: null}
{id: 0} vs {id: -1}
Duplicate Keys
{"id": 1, "id": 2}
Parser precedence varies; may bypass validation. Also test default argument values.
Extra Fields
Send unexpected keys in input objects; backends may pass them to resolvers or downstream logic.
Cursor Manipulation
Decode cursors (usually base64) to:
- Manipulate offsets/IDs
- Skip filters
- Cross ownership boundaries
Directive Abuse
@defer/@stream
query {
me { id }
... @defer { adminPanel { secrets } }
}
May return gated data in incremental delivery. Confirm server supports incremental delivery.
Custom Directives
@auth, @private and similar directives often annotate intent but do not enforce—verify actual checks in each resolver path.
Complexity Attacks
Fragment Bombs
fragment x on User { friends { ...x } }
query { me { ...x } }
Test depth/complexity limits, query cost analyzers, timeouts.
Wide Selection Sets
Abuse selection sets and fragments to force overfetching of sensitive subfields.
Federation Exploitation
SDL Exposure
query { _service { sdl } }
Entity Materialization
query {
_entities(representations:[
{__typename:"User", id:"TARGET_ID"}
]) { ... on User { email roles } }
}
Gateway may enforce auth; subgraph resolvers may not. Look for cross-subgraph IDOR via inconsistent ownership checks.
Subscription Security
- Authorization at handshake only, not per-message
- Subscribe to other users' channels via filter args
- Cross-tenant event leakage
- Abuse filter args in subscription resolvers to reference foreign IDs
Persisted Query Abuse
- APQ hashes leaked from client bundles
- Replay privileged operations with attacker variables
- Hash bruteforce for common operations
- Validate hash→operation mapping enforces principal and operation allowlists
CORS & CSRF
- Cookie-auth with GET queries enables CSRF on mutations via query parameters
- GraphiQL/Playground cross-origin with credentials leaks data
- Missing SameSite and origin validation
File Uploads
GraphQL multipart spec:
- Multiple Upload scalars
- Filename/path traversal tricks
- Unexpected content-types, oversize chunks
- Server-side ownership/scoping for returned URLs
WAF Evasion
Query Reshaping
- Comments and block strings (
"""...""") - Unicode escapes
- Alias/fragment indirection
- JSON variables vs inline args
- GET vs POST vs
application/graphql
Fragment Splitting
Split fields across fragments and inline spreads to avoid naive signatures:
fragment a on User { email }
fragment b on User { password }
query { me { ...a ...b } }
Bypass Techniques
Transport Switching
Content-Type: application/json
Content-Type: application/graphql
Content-Type: multipart/form-data
GET with query params
Timing & Rate Limits
- HTTP/2 multiplexing and connection reuse to widen timing windows
- Batching to bypass rate limits
Naming Tricks
- Case/underscore variations
- Unicode homoglyphs (server-dependent)
- Aliases masking sensitive field names
Cache Confusion
- CDN caching without Vary on Authorization
- Variable manipulation affecting cache keys
- Redirects and 304/206 behaviors leaking partial responses
Testing Methodology
- Fingerprint - Identify endpoints, transports, stack (Apollo, Hasura, etc.), GraphiQL exposure
- Schema mapping - Introspection or inference to build complete type graph
- Principal matrix - Collect tokens for unauth, user, premium, admin roles with at least one valid object ID per subject
- Field sweep - Test each resolver with owned vs foreign IDs via aliases in same request
- Transport parity - Verify same auth on HTTP, WebSocket, persisted queries
- Federation probe - Test
_serviceand_entitiesfor subgraph auth gaps - Edge cases - Cursors, @defer/@stream, subscriptions, file uploads
Validation Requirements
- Paired requests (owner vs non-owner) showing unauthorized access
- Resolver-level bypass: parent checks present, child field exposes data
- Transport parity proof: HTTP and WebSocket for same operation
- Federation bypass:
_entitiesaccessing data without subgraph auth - Minimal payloads with exact selection sets and variable shapes
- Document exact resolver paths that missed enforcement