“It’s GraphQL, It’s Typed, It’s Safe” – One Introspection Query Later
We are running a security assessment on a well-known SaaS system with a GraphQL API. The team there says: “we are safe – GraphQL is typed, everything is locked down”. I smile. I have heard that more than once.
Stage one: mapping. In REST you go looking for Swagger. In GraphQL you use introspection:
{ __schema { types { name fields { name } } } }
The server answers politely, and now I have a full blueprint – every type, every mutation.
Stage two: finding an interesting spot. Among all the mutations there is something called updateUser, with the fields id, isAdmin, email, name. The normal UI only lets you change name and email. So I ask myself: “what happens if I send a mutation with isAdmin=true?”
I run:
mutation { updateUser(id: 6, isAdmin: true) { id isAdmin } }
and the response:
{ "data": { "updateUser": { "id": 6, "isAdmin": true } } }
Boom – my user is now an admin.
There was no need to break into the application or to guess passwords. The system itself, with ordinary user privileges, allows an update to a critical field that is never checked at the authorization level. The UI hides it, but the API itself is not blocked.
The recommendations
- Disable introspection in production.
- Define authorization checks at the field level, not just “a user is updating themselves”.
- Run automated tests against every new mutation – especially ones that change permissions.
The moral: one introspection query and one line of mutation is all it takes to open a door to admin. Not because GraphQL is a problem, but because the security assumption was “if it is not in the UI, it is safe”.
I first shared a version of this as a LinkedIn post on 2025-07-28. It is republished here, lightly edited, so it is easier to find and reference. — Erez Metula
