Skip to content

Detox Technologies

GraphQL Penetration Testing Checklist: Authorization, DoS and API Risks

GraphQL gives clients flexibility: one endpoint can expose a rich schema, nested relationships, queries, mutations and subscriptions. That flexibility improves developer experience, but it also creates a concentrated attack surface. A tester who understands the schema can explore objects, request fields the user interface never displays, combine operations and create expensive nested queries.

GraphQL is not inherently insecure. Most serious issues come from authorization gaps, unsafe resolver behaviour, excessive resource use and production configurations that reveal more than intended. The OWASP GraphQL Cheat Sheet highlights input validation, access control, query limits, batching protections and secure configuration as core controls.

This GraphQL penetration testing checklist explains how to assess those controls methodically and how to produce findings developers can reproduce and fix.

How GraphQL changes API testing

REST APIs commonly expose many paths. GraphQL often exposes a single endpoint such as /graphql, while the request body selects operations and fields. Security testing therefore focuses on the schema, resolvers and authorization logic behind that endpoint.

A typical request can include:

  • A query or mutation name.
  • Nested fields and relationships.
  • Variables containing object identifiers and input data.
  • Fragments that reuse field selections.
  • Aliases that request the same operation repeatedly.
  • Multiple operations in one document.
  • Batched requests in one HTTP call.

Network controls that count requests may see only one request even when the server performs hundreds of resolver operations. A tester must evaluate cost and authorization at the application level.

Define scope before testing

GraphQL testing can consume significant resources if depth, aliases or batching are unbounded. Agree on:

  • Target endpoints and environments.
  • Test accounts for multiple users, roles and tenants.
  • Whether introspection is expected to be public.
  • Allowed query depth, complexity and request rate.
  • Prohibited destructive mutations.
  • Synthetic records that can be modified safely.
  • Monitoring contacts and stop conditions.
  • Whether subscriptions, file uploads and underlying REST APIs are included.

Use a representative non-production environment where possible. If production testing is required, begin with low-cost queries and strict limits.

Step 1: Discover GraphQL endpoints

Common paths include /graphql, /api/graphql, /v1/graphql and product-specific gateways. Review JavaScript bundles, mobile applications, API documentation, proxy history and network traffic.

Confirm supported HTTP methods and content types. Some deployments secure POST but allow GET queries, or validate JSON while accepting application/graphql differently. Check alternate hosts, versions and legacy endpoints; one gateway may be protected while an older direct endpoint remains exposed.

Identify GraphiQL, Playground or other interactive consoles. These are useful during development but may reveal schema and environment details in production.

Step 2: Map the schema safely

Introspection allows a client to ask the server about types, fields, arguments and operations. Public APIs may intentionally enable it; private production APIs may restrict it. Enabled introspection is not automatically a vulnerability, but it lowers discovery effort and must match the intended exposure.

If introspection is disabled, observe normal application queries, documentation and client bundles. Do not assume the schema is secret: field suggestions, error messages, persisted-query registries and mobile clients can still reveal it.

Build an inventory of:

  • Queries, mutations and subscriptions.
  • Object types and direct node lookups.
  • User, tenant, role and ownership identifiers.
  • Administrative or internal fields.
  • File upload and URL-fetching inputs.
  • Search, filter, sort and pagination arguments.
  • Fields containing personal, financial or secret data.

Step 3: Test authentication

Send known operations without a token, with an invalid token, after logout and after expiry. Compare behaviour across HTTP, WebSocket subscriptions and any alternate endpoint.

Check whether authentication is enforced consistently at the gateway and resolver. A top-level query may require login while a nested resolver accidentally returns sensitive fields. Verify refresh-token handling, session invalidation and tenant selection.

Error responses should not distinguish valid users unnecessarily or disclose token internals, stack traces and configuration.

Step 4: Test object-level authorization

Broken Object Level Authorization (BOLA), also known as IDOR in many contexts, occurs when the server accepts an object identifier without verifying that the caller may access that object.

Use two test identities. Record an object created by User A, then request it as User B through every path that can reach it:

  • Direct queries such as user(id:) or order(id:).
  • Global node(id:) fields.
  • Nested relationships.
  • Search and list queries.
  • Mutations for update, delete, share or export.
  • Aliases and batches.
  • Subscriptions and cached results.

Test both predictable IDs and opaque IDs. UUIDs make guessing harder but do not replace authorization. The server must validate ownership or policy on every resolver path.

Step 5: Test function-level authorization

Broken Function Level Authorization occurs when a user can invoke an operation intended for a more privileged role. Look for administrative mutations, moderation actions, account-state changes, exports, pricing controls and permission management.

An operation name such as adminDeleteUser should not rely on the client hiding it. Try it with ordinary roles and through aliases. Verify that authorization occurs server-side and that nested resolvers do not inherit unsafe trust from a privileged parent.

Step 6: Test field-level authorization and excessive exposure

GraphQL lets clients request fields the normal interface never uses. A User type might expose internal IDs, role flags, password hashes, reset tokens or private notes even though the frontend requests only name and avatar.

For each sensitive type, compare fields returned to different roles. Test whether restricted fields appear through fragments, nested objects, node lookups, search results, errors or subscriptions.

Avoid relying on the client to omit confidential fields. Resolver-level or policy-level controls must prevent unauthorised retrieval.

Step 7: Test mutations and mass assignment

Input objects may accept fields that users should not control, such as role, ownerId, accountBalance, isVerified or tenantId. Submit additional fields and observe whether they are ignored, rejected or applied.

Test creation, update, deletion and relationship changes. Verify object ownership before and after the mutation. Check whether a user can transfer a record to another tenant, modify immutable properties or invoke a mutation through an alternate input type.

Use synthetic records and avoid destructive operations outside the agreed scope.

Step 8: Test query depth and complexity

Nested relationships can create expensive resolver chains. Circular schemas may permit deeply recursive queries. A shallow query can also be costly if it requests large lists at several levels.

Increase depth and breadth gradually within agreed limits. Observe response time, CPU, memory, database calls and downstream requests. Confirm that the application enforces:

  • Maximum depth.
  • Query cost or complexity budgets.
  • Pagination and maximum page sizes.
  • Resolver and request timeouts.
  • Per-user or per-token rate limits.
  • Resource limits and circuit breakers.

Cost should reflect actual work. A simple field backed by an expensive external API may deserve a higher score than several in-memory fields.

Step 9: Test aliases and batching

Aliases can repeat a field or mutation many times in one document. HTTP batching can carry several operations in one request. Attackers may use these features to accelerate enumeration, credential attacks or resource exhaustion while bypassing request-count limits.

Test limits on aliases, operations, objects and batches. Sensitive operations such as login, OTP verification and password reset need controls at the resolver and account level, not only at the reverse proxy.

Log each underlying operation so a batch of one hundred attempts does not appear as one harmless event.

Step 10: Test injection and unsafe resolver inputs

Resolvers may pass arguments to SQL, NoSQL, operating-system commands, templates, search engines or downstream HTTP services. GraphQL’s type system reduces some malformed input, but it does not make unsafe backend construction secure.

Test strings, lists, custom scalars, filters, sort expressions and nested input objects. Look for SQL/NoSQL injection, command injection, server-side request forgery, path traversal and template injection.

Use parameterised APIs, allowlists and business validation. Custom scalars should validate format and range rather than merely convert input.

Step 11: Test error handling and information leakage

Unexpected fields, invalid types and resolver failures may return stack traces, database messages, internal paths, service names or code locations. Send malformed but safe requests and compare production responses.

Clients should receive a stable, minimal error. Detailed diagnostics belong in protected server logs with a correlation identifier. Ensure errors do not echo secrets, tokens or private data supplied by downstream services.

Step 12: Review introspection and developer tooling

If public introspection is unnecessary, restrict it appropriately. Disable unauthenticated GraphiQL or Playground in production. Remove debug extensions, tracing details and schema comments that expose internal assumptions or secrets.

Do not treat disabled introspection as a primary defence. Attackers can learn operations from clients and error suggestions. Authorization and resource limits must remain effective even with full schema knowledge.

Step 13: Test file uploads and URL fetching

GraphQL upload implementations may accept multipart requests. Validate file size, count, type, extension, storage path and malware handling. Store files outside executable web paths and use generated names.

If a resolver fetches a user-supplied URL, test server-side request forgery protections. Allowlist destinations where possible, block internal and metadata addresses, validate redirects and limit response size and time.

Step 14: Test subscriptions

Subscriptions introduce long-lived connections and event-level authorization. Verify authentication during connection and re-authorize when roles, sessions or tenant membership change.

Test whether User B can subscribe to User A’s events by changing identifiers. Confirm that logout and revocation close active access. Apply connection, subscription and event-rate limits.

Step 15: Assess underlying and alternate APIs

GraphQL may sit in front of REST or microservices. A secure GraphQL resolver does not help if the underlying endpoint is directly reachable and trusts gateway headers from any source.

Identify direct service paths and compare their authorization. Restrict network access, authenticate service-to-service calls and prevent clients from forging identity headers. Include mobile and legacy API versions in the scope where appropriate.

A concise GraphQL pentest checklist

Use this summary during planning:

  1. Discover every GraphQL endpoint, method and transport.
  2. Map queries, mutations, subscriptions and sensitive fields.
  3. Verify authentication, expiry, logout and revocation.
  4. Test BOLA with at least two user or tenant identities.
  5. Test privileged operations using ordinary roles.
  6. Check field-level authorization and hidden sensitive data.
  7. Test input objects for mass assignment.
  8. Enforce depth, complexity, pagination and timeouts.
  9. Limit aliases, batches and repeated sensitive operations.
  10. Test resolver inputs for injection and SSRF.
  11. Remove detailed production errors and debug tooling.
  12. Review introspection according to intended exposure.
  13. Validate uploads and URL-fetching resolvers.
  14. Authorize subscriptions and terminate revoked sessions.
  15. Secure direct downstream and legacy APIs.
  16. Log user, operation, variables, policy result and resource safely.
  17. Retest confirmed findings after remediation.

Reporting findings developers can fix

A useful GraphQL finding should include the operation, variables, test identity, affected object, expected policy, actual response and business impact. Provide the smallest safe proof of concept and redact sensitive records.

For resource-exhaustion issues, report the measured effect and agreed limits rather than publishing a damaging payload. For authorization, explain whether the weakness is object-, function- or field-level and identify other resolver paths that may share the same policy gap.

Recommend systemic fixes. Adding a check to one resolver is insufficient if twenty resolvers access the same object without a common authorization layer.

Integrating GraphQL testing into delivery

Add authorization regression tests with multiple identities. Store representative queries for depth, aliases and field access. Run schema-diff checks to identify new sensitive fields and mutations. Apply static analysis to resolvers and dynamic tests to a deployed environment.

An independent web application VAPT can combine GraphQL testing with browser workflows, authentication and business logic. Broader penetration testing services can include the gateway, downstream APIs, cloud identity and network exposure.

For REST and cloud endpoints, use the related cloud API security testing checklist. GraphQL should be tested as part of the application’s complete trust model, not as an isolated endpoint.

Frequently asked questions

Is enabled GraphQL introspection always a vulnerability?

No. Public APIs may intentionally expose schema information. Risk depends on intended access and the sensitivity of what is revealed. Strong authorization is required regardless of introspection status.

Does a Web Application Firewall protect GraphQL APIs?

A WAF can help with known payloads and request limits, but it may see one request containing many expensive or repeated resolver operations. Application-level authorization, cost analysis and batching limits remain necessary.

Are UUIDs enough to prevent BOLA?

No. Unpredictable identifiers reduce casual enumeration but can leak through logs, links and other APIs. Every resolver must verify that the caller is authorised for the requested object.

How often should GraphQL security testing occur?

Test before launch, after major schema or authorization changes and periodically based on exposure. Run automated authorization and complexity regression tests in CI, supplemented by manual penetration testing for business logic.

What should teams fix first?

Prioritise cross-tenant or privileged authorization failures, sensitive field exposure and resource-exhaustion paths that affect availability. Then address information leakage and configuration weaknesses.

Conclusion

GraphQL concentrates powerful API capabilities behind a flexible query language. Secure deployments assume that clients know the schema and may construct requests the frontend never sends.

The strongest controls are server-side authorization at every object, function and field; strict input handling; bounded query cost; safe batching; minimal errors; and complete operation-level logging. Penetration testing validates how those controls interact across resolvers, identities and downstream services.

Start with two users, one sensitive object and every path that can reach it. Then examine privileged mutations, hidden fields and expensive operations. This evidence-led approach finds the issues most likely to produce real data exposure, privilege escalation or denial of service—and gives developers a clear path to remediation.

Discover more from Detox Technologies

Subscribe now to keep reading and get access to the full archive.

Continue reading

Verified by MonsterInsights