The Field the Error Named: GraphQL Suggestions Leak a Staff-Only Field

Web & API Security Level 3/4 ~5 min October 31, 2026

The challenge

Wrenmoor's GraphQL API hides its field list. Guess a field name: errors suggest the real one. Bring back order 1042's refund override code.

What you'll learn

  • Explain what GraphQL introspection exposes and why switching it off is not enough
  • Use graphql-js field suggestions to discover field names a schema hides
  • Read Apollo Server error responses: status code, message and error code
  • Recognize a missing field-level authorization check
  • Hide suggestions in production and authorize sensitive fields

Skills tested

API security testingGraphQLRequest tampering

Prerequisites

  • What a GraphQL query looks like
  • Reading a JSON HTTP response

How it works

A GraphQL API serves one schema: every type and field a client may ask for. Introspection (__schema, __type) lets anyone download that schema, so production servers often switch it off. Apollo Server does so by default when NODE_ENV=production and answers an introspection query with GraphQL introspection is not allowed by Apollo Server.

Switching introspection off does not remove the schema. Every query is still validated against it, and graphql-js answers an unknown field with the closest real names: Cannot query field "overrideCode" on type "Order". Did you mean "agentOverrideCode"? One wrong guess at a time, the hidden schema comes back. Clairvoyance automates this with a wordlist, and PortSwigger's Web Security Academy covers it in its GraphQL topic.

Here the leaked name is a support-dashboard field on the same Order type the storefront uses. The resolver only checks that the order belongs to the caller, not who may read each field, so knowing the name is enough to read it. The OWASP API Security Top 10 calls this Broken Object Property Level Authorization (API3:2023).

Common mistakes

  • Stopping at the introspection error. It only hides the field list; field validation still talks.
  • Guessing on the wrong type. { agentOverrideCode } at the top level asks the Query type, which has no such field. Ask inside order(id: 1042) { ... }.
  • Breaking the JSON body. A double quote inside the query, as in __type(name: "Order"), must be written \"; otherwise Express rejects the body with a 400 page before GraphQL sees it.
  • Trying another order. order(id: 1041) is refused: the object check works. The leak is a field on your own order.
  • Answering the field name, or the old code. The answer is the value of agentOverrideCode; the OVR-TEST-1188 in agentNote has expired.

How to defend against it

Close the leak and add the missing check.

  • Hide suggestions in production: new ApolloServer({ hideSchemaDetailsFromClientErrors: true }) (Apollo Server 4.11 and later), or graphql-armor's block-field-suggestions plugin on other servers.
  • Authorize fields, not just objects: give staff-only fields their own role check in the resolver (or an auth directive), or serve the support dashboard from a separate schema.
  • Treat the schema as public anyway: introspection off and hidden suggestions slow an attacker down, they do not protect data. For your own clients, a persisted-query allowlist stops arbitrary queries altogether.

Full solution

Pro and Max members unlock the complete step-by-step walkthrough.

Go Pro

Community stats

126 completions
77% success rate
M2F14M3 First blood

Related Daily Hacks

36,000+ Hackers Real labs Free
Start Hacking Free or solve today's hack, no account needed