GraphQL API Security Testing: Resolver Access, Batching, and Cost
Short answer
GraphQL security is not a route-level access check. One endpoint can execute many queries, mutations, and nested resolvers, so authorization must remain consistent down to fields and relationship edges. A useful assessment reviews schema discovery, resolver behavior, batching, and resource consumption as one workflow.
GraphQL’s flexible query model helps product teams, but it also enables unexpected data combinations and expensive operations. Access checks that appear complete across separate REST endpoints can be missed when the same object is reachable through multiple GraphQL resolver paths.
Map the schema and resolver paths
Inventory queries, mutations, subscriptions, custom scalars, interfaces, and unions by role and version. Even with introspection disabled in production, client operations, error messages, documentation, and persisted-query records can reveal parts of the schema. Disabling introspection does not fix authorization; every resolver still needs the correct policy.
Try reaching an object through two paths, such as currentUser { invoices } and a direct invoice(id) query. A list resolver may filter by tenant while a single-object resolver does not. The distinction between graph edges and nodes matters: permission to list a relationship does not automatically grant permission to read every returned object.
For mutations, inspect nested input objects, not only the root field. Can a client change ownerId, tenantId, role, status, or a server-controlled price? Can the mutation return success while changing another object as a side effect? Verify both action permission and target ownership for every sensitive change.
Build an authorization matrix
Use accounts A and B, separate tenants, and administrator/member/reader roles. Exercise each query in these forms:
| Variant | Control to verify |
|---|---|
| List field | ACL is applied to every returned node |
| Direct ID | Another user’s or tenant’s object is denied |
| Nested relation | Parent permission is not incorrectly inherited by children |
| Mutation input | Client-controlled ownership and role fields are ignored |
| Alias or fragment | Alternate operation shapes use the same policy |
| Subscription | Role or membership changes affect an open connection |
A route-level check is not enough. Authorization should have a shared, testable design in resolvers, domain services, or data-access boundaries. If each resolver invents its own decision, code review is more likely to miss one.
Batching, depth, and query cost
GraphQL batching can submit many operations or object lookups in one HTTP request. If rate limiting counts only IPs or HTTP requests, expensive work inside one request can remain invisible. Bound batch size and total operation count for sensitive fields such as login, OTP, user search, and password recovery.
Evaluate nested depth, list size, pagination limits, and resolver fan-out together. A depth limit alone is insufficient: a shallow query may return millions of nodes, and many aliases can repeatedly invoke the same resolver. Review query-cost analysis, timeouts, per-user rate limits, and database load behavior as a set. When a limit is exceeded, expect a clear safe rejection rather than partial data or silent truncation.
Errors and production configuration
Check whether resolver exceptions disclose stack traces, SQL details, filesystem paths, or internal service names. Suggestions and introspection are not automatically vulnerabilities; impact depends on whether sensitive operations are available anonymously and expose live data. Confirm GraphiQL and debug tools are disabled or restricted in production deployment profiles.
If persisted queries are used, test whether unregistered operations are accepted in production and whether the allowlist blocks unauthorized operations. An allowlist does not replace resolver authorization; a permitted operation can still expose user-specific data to the wrong caller.
Safe assessment sequence
- Create an access matrix for roles and sensitive graph objects.
- Reach the same resource through lists, direct IDs, nested relations, and mutations.
- Exercise aliases, fragments, batching, depth, page size, and cost controls.
- Observe how subscriptions react to session and role changes.
- Record redacted evidence showing the query, role, resource owner, and returned or changed data.
- Retest both the denied cross-account path and the legitimate authorized flow.
Gözlemlenebilirlik ve üretim limitleri
Resolver bazlı telemetry; hangi operasyonun, hangi rol ve tenant ile, hangi maliyetle çalıştığını açıklayabilmelidir. Ham kişisel veriyi veya tüm değişkenleri loglamak yerine operasyon adı/hash’i, sonuç boyutu, resolver süresi ve policy kararı gibi alanları kullanın. Böylece pahalı veya anormal sorgular görünür olurken loglar yeni bir veri sızıntısı yüzeyi oluşturmaz.
GraphQL gateway, uygulama ve data loader limitleri birlikte tasarlanmalıdır. Gateway tek isteği kabul ederken resolver fan-out veritabanını tüketebilir; uygulama timeout’u olsa bile iptal edilmeyen alt sorgular çalışmayı sürdürebilir. Query iptalinin bağlantı havuzu ve alt servis çağrılarına yayıldığını, limit aşımlarının ölçülebilir ve tutarlı şekilde reddedildiğini kontrol edin.
For an assessment that combines GraphQL with identity, object authorization, and business workflows, see our API security assessment.
Security Validation
Have you tested this risk in your own system?
Eresus Security delivers real exploit evidence through penetration testing, AI agent security, and red team operations.
Request a pilot testAI Security Starter Training
Request a practical checklist for prompt injection, RAG data leakage, MCP risks, and model-file security before launch.
Related Research
Related Services
Scope Estimator
Get a rough engagement size before the scoping call.
Estimated engagement
5–7 days