GraphQL Security: Preventing Data Exposure and IDOR

Eviant
4 min read
GraphQL API Security IDOR Data Exposure Application Security Web Security

GraphQL’s flexibility makes it attractive for API development, but that same flexibility creates security blind spots. The two most common vulnerabilities we find in GraphQL implementations are exposed introspection in production (revealing your entire API schema to attackers) and broken object-level authorization that allows users to access other users’ data. These aren’t theoretical—we’ve seen production APIs where a simple introspection query exposes sensitive customer data structures, and where changing a user ID in a query grants access to arbitrary accounts.

Introspection Exposes Your API Schema

GraphQL introspection is a feature that lets clients query the API schema itself—every type, field, mutation, and argument. This is useful during development, but in production it hands attackers a complete map of your API surface.

Test if introspection is enabled by sending this query:

query IntrospectionQuery {
  __schema {
    types {
      name
      fields {
        name
        type {
          name
        }
      }
    }
  }
}

If this returns your full schema, an attacker now knows every query, mutation, and data field available. They can identify sensitive endpoints you didn’t intend to expose (admin mutations, internal data types) and craft targeted attacks.

Real-world example: During a Vulnerability testing engagements for an API, introspection was used to discover that an internalNotes field existed on user objects in this application. The frontend didn’t display this field, but querying it directly via GraphQL returned comments that should not be accessible by customers.

Introspection should be disabled for public production endpoints. For APIs that need introspection for tooling (GraphQL Playground, client code generation), restrict it to authenticated internal users or separate development endpoints.

IDOR: Accessing Other Users’ Data

Insecure Direct Object Reference (IDOR) vulnerabilities occur when GraphQL queries accept object IDs without verifying the requesting user has permission to access that object. This is commonly found with GraphQL implementations and is a critical vulnerability.

Common vulnerable pattern:

query GetUserProfile {
  user(id: 123) {
    email
    phone
    address
    orders {
      id
      total
    }
  }
}

If the API doesn’t check that the authenticated user is authorized to view user ID 123’s data, you can iterate through IDs to extract all users’ information.

Testing for IDOR:

  1. Authenticate as User A and capture a legitimate query (e.g., user(id: 100))
  2. Modify the ID to another user’s ID (e.g., user(id: 101))
  3. If the query succeeds and returns User B’s data, you’ve found IDOR

Batch IDOR testing is particularly effective in GraphQL since you can query multiple IDs in one request:

query BatchUsers {
  user1: user(id: 100) { email }
  user2: user(id: 101) { email }
  user3: user(id: 102) { email }
  user4: user(id: 103) { email }
  # ... up to hundreds of aliases
}

This allows exfiltrating entire databases in a few requests if authorization isn’t enforced.

Why GraphQL makes IDOR worse: Traditional REST APIs require separate endpoints for different resources (/api/users/123, /api/orders/456). GraphQL lets you traverse relationships in a single query, so one IDOR vulnerability can expose nested objects:

query ExploitIDOR {
  user(id: 101) {
    email
    orders {
      items {
        product {
          name
          internalCost  # Sensitive field
        }
      }
    }
    paymentMethods {
      lastFour
      expiryDate
    }
  }
}

A single vulnerable user resolver now exposes orders, products, internal pricing, and payment data.

Implementing Proper Authorization

Authorization must be enforced at the resolver level for every object type, not just the top-level query. If your user query checks authorization but nested fields like orders don’t, attackers access data by requesting deeper objects.

Resolver-level authorization pattern:

const resolvers = {
  Query: {
    user: async (parent, { id }, context) => {
      // Check if requesting user can access this user object
      if (context.user.id !== id && !context.user.isAdmin) {
        throw new Error('Unauthorized');
      }
      return getUserById(id);
    }
  },
  User: {
    orders: async (user, args, context) => {
      // Re-check authorization for nested field
      if (context.user.id !== user.id && !context.user.isAdmin) {
        throw new Error('Unauthorized');
      }
      return getOrdersForUser(user.id);
    }
  }
};

Use field-level permissions: Sensitive fields like internalNotes or ssn should require elevated permissions even if the parent object is accessible. Implement field-level authorization directives:

type User {
  id: ID!
  name: String
  email: String @auth(requires: OWNER)
  ssn: String @auth(requires: ADMIN)
  internalNotes: String @auth(requires: STAFF)
}

Avoid exposing internal IDs: Use opaque tokens or UUIDs instead of sequential integers. If your user IDs are 1, 2, 3, attackers trivially enumerate all accounts. Random UUIDs make enumeration impractical.

Testing Your GraphQL API

Automated scanning tools:

  • InQL (Burp Suite extension): Performs introspection and generates queries for all discovered endpoints
  • GraphQL Voyager: Visualizes schema relationships to identify sensitive data paths
  • Escape GraphQL Security Scanner: Open-source tool that detects common GraphQL vulnerabilities

Key Takeaways

  • Disable introspection in production—it exposes your entire API schema to attackers and reveals sensitive fields your frontend doesn’t use.
  • IDOR is a common GraphQL critical vulnerability—implement authorization checks at every resolver, not just top-level queries.
  • Nested objects bypass authorization—if you protect the user query but not user.orders, attackers access data via relationships.
  • Test with batch queries—GraphQL allows hundreds of IDOR attempts in one request via aliasing, accelerating data exfiltration.

Need help securing your GraphQL APIs? Contact Eviant for application security assessments and secure API design reviews.

Share this article:

Ready to Work Together?

Let's discuss how we can help protect your business and achieve your security goals.

Get In Touch