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
Keep reading
More from the blog
Authorization and Access Control
Configured Is Not Enforced
AWS shipped four condition keys for DynamoDB and did not evaluate them at runtime. Your policy sat there, looked excellent in code,…
Read itAuthorization and Access Control
A JWT That Trusted the Client: How One Shortcut Became Full Admin Access
A penetration test where a JWT carried the user's role in its payload and the server trusted it. Changing one field -…
Read itAuthorization and Access Control
A Valid Token Is Not a Permission: The OAuth Scope Nobody Checked
The Authorization Code flow was textbook. Then the server accepted any valid access token on an admin endpoint, whatever scope it carried…
Read itTell us what the system does and what worries you.
If a penetration test is not what you need yet, we will say so.