GraphQL security
In API security, GraphQL security is the set of controls needed by an API where the client, not the server, decides the shape of each response. One endpoint, a typed schema and client-composed queries break several assumptions that REST security tooling and REST reviewers rely on.
How it works
A GraphQL service exposes a single endpoint and a schema describing every type, field, argument and relationship. The client sends a query naming the fields it wants; the server resolves each field, often with a separate function per field, and returns exactly that shape. Introspection is part of the specification: the schema can be queried for its own definition, which is how development tooling discovers the API.
Because resolution is per field, authorisation has to be per field too. A check performed once at the entry point cannot be correct, since a single query can traverse from an object the caller owns to an object they do not through a relationship the schema exposes.
What goes wrong
Four things, in the order we usually find them. Introspection left enabled in production, which hands us the complete map of types and arguments including the fields no client uses; disabling it is worth doing, but it is obfuscation, not a control, because the schema can be recovered by other means. Authorisation implemented at the resolver for the root object and missing on nested resolvers, which is broken object level authorisation reached through a relationship rather than through an identifier.
Then cost. Nested and recursive selections let a small request generate an enormous amount of work, and aliases let a single request repeat the same field hundreds of times, which turns one HTTP request into hundreds of operations and quietly defeats any rate limiting that counts requests. Query depth limits, complexity scoring and persisted queries are the answers, and cost must be counted in resolver work rather than in requests.
Finally, batching multiple operations in one request is a well-known way to brute-force codes and credentials without tripping counters that were written for one attempt per request.
Where this shows up in an audit
We recover the schema, build the query set from it, and then test relationships rather than endpoints, because the traversal is where the flaw lives. The report names the query, the path through the graph, and the data returned that the caller was not entitled to, plus the measured cost of the most expensive query we could compose. We also check whether the endpoint accepts operations over GET, which reopens caching and CSRF questions. This is part of how we test a GraphQL endpoint.