GraphQL Hacking: The Complete Practical Guide

What Is GraphQL? A Hacker's Mental Model

GraphQL for security testers: one endpoint, client-shaped responses, queries vs mutations vs subscriptions, and why the typed schema helps an attacker.

Published

Before hacking GraphQL you need the model, because it is different enough from REST that the usual instincts mislead. This page covers how GraphQL is shaped and, specifically, why its design is a gift to an attacker. Practice on a free live target.

One endpoint, one POST, a typed query language

A REST API gives you many URLs and HTTP verbs. A GraphQL API gives you one endpoint, usually reached by a single POST, and the operation you want is described in the request body. There is nothing to traverse in the path and no verb to tamper with. Instead, the body carries a query written in GraphQL's own language, and the server returns JSON shaped exactly like your query.

POST /graphql HTTP/1.1
Host: target.example
Content-Type: application/json
 
{"query":"{ me { id name email } }"}
HTTP/1.1 200 OK
Content-Type: application/json
 
{"data":{"me":{"id":"1","name":"Ada","email":"[email protected]"}}}

You asked for three fields and got exactly those three. That client-controlled response shape is the core idea.

Queries, mutations, subscriptions

GraphQL has three operation kinds:

  • Query reads data (no side effects, like a GET).
  • Mutation changes data (create, update, delete, like a POST/PUT/DELETE).
  • Subscription streams data over a persistent connection (often WebSockets).

For testing, mutations are the high-value targets because they change state, and they are frequently where authorization is thin or missing.

The client picks the response shape

Because you choose the fields, you can ask for more than any screen in the UI ever shows. If a User type exposes an email or a passwordResetToken field, you can request it directly even if no page displays it. The UI is not the attack surface. The schema is.

Schema, types, fields, resolvers

Four terms you will use constantly:

  • Schema: the full typed definition of everything the API exposes.
  • Type: a named object with fields (User, Order, Product).
  • Field: a single piece of data on a type (User.email), which can take arguments.
  • Resolver: the server function that produces a field's value, often from a database.

Authorization and input handling live in the resolver. GraphQL checks that your argument matches its declared type and then passes it to the resolver untouched. So the language is rarely the bug. The resolver that skips an authorization check or concatenates your input into a query is.

Why the type system helps an attacker

GraphQL is self-describing. Through introspection, the server will tell you every type, field, argument, and mutation it has, which is a free and complete map of the attack surface, including operations the UI never calls. Even when introspection is disabled, field suggestions often leak the names anyway. No other common API style volunteers its entire structure like this.

That map is where the rest of this cluster starts.

Where to go next

Now find a real endpoint and confirm it in finding GraphQL endpoints, then pull the whole schema in introspection and schema discovery. Or jump straight to a free live target.

← Back to GraphQL Hacking: The Complete Practical Guide