GraphQL API vulnerabilities

GraphQL API Vulnerabilities

GraphQL is a query language for APIs that was created by Facebook in 2012 and open sourced in 2015. It provides an alternative to REST APIs and enables developers to request exactly the data they need from an API. While GraphQL offers many benefits over traditional REST APIs, like improved performance and developer experience, it also introduces new potential vulnerabilities that developers need to be aware of when designing and securing GraphQL APIs. In this comprehensive guide, we will explore the most common GraphQL vulnerabilities, how they can be exploited, and how to properly secure GraphQL APIs against attacks.

What is GraphQL?

GraphQL is a strongly-typed query language that allows clients to request precisely the data they need from an API. It replaces the typical collections of REST endpoints with a single endpoint that accepts queries.

A GraphQL query is used to retrieve data from a GraphQL schema. The schema defines the structure of an API and the relationships between different entities. It specifies the types of data that can be queried as well as the operations for querying and manipulating that data.

Some key advantages of GraphQL include:

  • Flexibility – Clients can request only the data they need, avoiding over and under fetching issues common with REST.
  • Type safety – The GraphQL schema defines types for API data, enabling early detection of errors.
  • Hierarchical – GraphQL structures APIs as graphs of data, mirroring how apps use data.
  • Efficient – Clients can reduce API requests by fetching more data in a single call.
  • Developer experience – GraphQL APIs are self-documenting and easy for developers to consume.
  • Evolvable – GraphQL schemas can be added to without impacting existing queries.

Common GraphQL Vulnerabilities

While GraphQL introduces many benefits, it also comes with its own unique security challenges. Some common GraphQL vulnerabilities include:

  • Broken Authentication and Access Controls
  • Server Side Request Forgery (SSRF)
  • Denial of Service
  • Introspection Query Information Leaks
  • Injection Attacks
  • Rate Limit Bypass

We will cover each of these common vulnerabilities in more detail below.

Broken Authentication and Access Controls

Proper authentication and authorization needs to be implemented when designing a GraphQL API, just like with a REST API. Some common issues include:

  • Failing to protect resolvers: Resolvers in GraphQL fulfill a similar role to controllers in a REST API. They contain the application logic and handle fetching data from databases and other back end systems. Resolvers need proper access controls to prevent data exposure.
  • Non-protected schemas: The GraphQL schema defines what queries can be executed against the API. It needs proper access controls to prevent anonymous users from querying sensitive data types and fields.
  • Leaked authorization rules: The GraphQL schema type system specifies what types a user can query and mutate. This inherently leaks some authorization rules that need to be protected.
  • Overly permissive queries: GraphQL queries allow filtering of fields within a type. Access controls need to be applied at the field level to prevent data exposure.

These issues can allow attackers to access unauthorized data or perform privileged actions. Proper authorization should be applied in GraphQL resolvers, schemas, and query analyzers.

Server Side Request Forgery (SSRF)

GraphQL APIs often need to interact with internal systems and external services to gather data to fulfill queries. For example, a GraphQL API may call out to internal databases or microservices. Improperly configured resolvers can lead to server-side request forgery (SSRF) vulnerabilities.

Attackers can potentially abuse insecure resolver logic to interact with internal systems or external sites like AWS metadata services. This can lead to unauthorized data access or denial of service.

Extra caution needs to be taken when allowing user input for fields like URLs or IPs that resolvers use to call out to other systems. Whitelists should be used to restrict allowed destinations.

Denial of Service

The flexibility of GraphQL queries can enable denial of service attacks in a few different ways:

  • Overly complex queries: GraphQL APIs can potentially accept complex nested queries. These queries can overload backend systems.
  • Resource intensive queries: Attackers can craft queries that target expensive database operations or trigger complex computations in resolvers.
  • Bulk requests: GraphQL allows multiple queries in a single request. Attackers can use this to overwhelm servers.

Limits should be imposed on queries to prevent denial of service attacks:

  • Query depth limits
  • Computational complexity thresholds
  • Rate limiting.

Backend systems also need proper resource management to gracefully handle spikes in traffic volume.

Introspection Query Information Leaks

The GraphQL introspection system allows querying an API for information about its schema and the available types, fields, arguments, and more. This is intended to enable developers to understand how to properly interact with the API.

However, introspection queries can leak sensitive information in production environments like:

  • Structure of the backend server
  • Internal architecture details
  • Comments with potential secrets or sensitive information
  • Hidden fields and arguments that attackers could potentially access

Introspection should be disabled in production. If it’s needed for public APIs, sensitive fields should be excluded from introspection results.

Injection Attacks

User-supplied input needs to always be properly sanitized and validated to prevent injection attacks, including GraphQL APIs. Some potential injection attack vectors include:

  • SQL injection if queries are built unsafely from user input
  • Code injection if exploitable vulnerabilities exist in underlying frameworks and libraries
  • GraphQL-specific attacks like stitching attack vectors together via aliases

The strong typing in GraphQL prevents some traditional injection attacks but input still needs proper encoding, sanitization, and validation before being passed to resolvers.

User input should be sanitized as early as possible, like when initially entering the GraphQL server code. Encoding, validation, and sanitization should also occur in resolvers. Context variables can assist in safely passing user input to resolvers.

Exploiting GraphQL APIs

GraphQL is a query language that provides an alternative to REST for building APIs. It offers many benefits, including improved performance, developer experience, and the ability to evolve APIs over time. However, GraphQL also introduces new security considerations compared to REST. In this comprehensive guide, we will explore common GraphQL vulnerabilities, including code examples of exploits and how to properly defend GraphQL APIs against them.

Just like any API that accepts user input, GraphQL endpoints are susceptible to injection attacks if improper input validation and sanitization is performed. Some common GraphQL injection risks include:

Exploiting GraphQL: Code Injection

If GraphQL queries are constructed unsafely from user input, code injection is possible in vulnerable GraphQL servers, frameworks or libraries:

# User input:

query {
  books(filter: '); require("child_process").exec("calc.exe"); -- ') {
    id
    name
  }
}

# Constructed query:

{
  books(filter: '); require("child_process").exec("calc.exe"); -- ') { 
    id
    name
  }
} 

This code injection attack leverages the user input to execute arbitrary system commands.

Exploiting GraphQL: SQL Injection

If the GraphQL API interacts with a SQL database, SQL injection is possible via unsafe construction of queries:

# User input:

query {
  users(filter: "' OR 1=1 --") {
    id 
    name
  }
}

# Constructed query: 

SELECT id, name 
FROM users
WHERE filter = '' OR 1=1 --'

The user input injects a tautology to return all users.

To prevent injection attacks:

  • Use prepared statements and query builders
  • Validate and sanitize all user input
  • Implement a whitelist for special fields like filter
  • Run restrictive CORS policies on GraphQL endpoints

GraphQL Broken Authentication and Authorization

Proper authentication and authorization needs to be implemented to secure GraphQL APIs. Some common vulnerabilities include:

Non-Protected Resolvers

Resolvers act similar to controllers in REST APIs. They execute backend logic and return data. Permissions need to be checked in resolvers:

// Resolver without auth check
const getUsers = () => {
  return User.find(); 
}

// Secure resolver
const getUsers = (parent, args, context) => {
  if(!context.isAuthenticated) {
    throw new Error('Not authenticated');
  }
  return User.find();
}

Leaked Authorization Rules

The GraphQL schema type system inherently reveals some auth rules. For example, the visibility of types and fields exposes if the current user can query them or not.

Overly Permissive Queries

Specific fields within a GraphQL type can be requested. Permissions should be defined at the field level:

type User {
  id: ID
  name: String
  email: String # Should be non-nullable for authorized users
}

type Query {
  user(id: ID): User
}

To properly secure authentication and authorization:

  • Use JSON Web Tokens or sessions to manage authentication
  • Implement role-based access control
  • Limit introspection queries
  • Protect resolvers with granular auth checks
  • Add non-null assertions to fields in the GraphQL schema for authorized users

GraphQL Example Denial of Service

The flexibility of GraphQL enables denial of service attacks through:

  • Complex nested queries
  • Queries that target expensive database operations
  • Resource intensive computations in resolvers
  • Bulk operations via aliases

Implement query complexity limits to prevent DoS attacks:

const { ApolloServer } = require('apollo-server');

const server = new ApolloServer({
  schema,
  validationRules: [
    queryComplexity({
      // Set max query complexity
      maximumComplexity: 1000
    })
  ]
});

Additional strategies include:

  • Restricting max query depth
  • Limiting number of aliases
  • Query cost analysis
  • Limiting total query execution time

Make sure backend systems like databases can handle traffic spikes gracefully.

Server Side Request Forgery (SSRF)

If GraphQL resolvers interact with internal or external services, SSRF is possible:

const getRepoDetails = async (repoName) => {

  const url = `https://api.github.com/repos/${repoName}`;

  const response = await axios.get(url);

  return response.data;

};

If repoName comes from user input, an attacker could force requests to internal services.

To prevent SSRF:

  • Validate and sanitize user input, especially URLs/IPs
  • Implement a whitelist for third party services that can be called
  • Limit ports/protocols that can be accessed

Information Leakage via Introspection

GraphQL introspection provides details about the schema, including types, fields, arguments, and descriptions.

Introspection should be disabled in production. If required, sensitive fields should be excluded.

const typeDefs = gql`

  type Query {
    users: [User!]!
  }

  type User {
    id: ID!
    name: String!
    # Exclude email from introspection
    email: String! 
  }
`;

const server = new ApolloServer({
  schema,
  introspection: true, // Disable for production

  // Exclude email from introspection
  extensions: ({ context }) => {
    if(context.excludeIntrospection) {
      return {
        redact: ['User.email']  
      }
    }
  }
});

The redact extension hides sensitive fields.

GraphQL provides a flexible alternative to REST APIs. However, unique security considerations need to be taken into account. Common vulnerabilities related to injection, authentication, denial of service and information disclosure can put API consumers and backend systems at risk. By properly implementing authentication, authorization, input validation, query complexity limits, and disabling introspection, companies can securely leverage GraphQL to provide next-generation API capabilities.

Rate Limit Bypass

APIs typically employ rate limiting to prevent brute force attacks or denial of service. In GraphQL, clients can bypass standard rate limits by spreading operations across aliases in a single request.

For example, normally a rate limit may allow 10 requests per minute. But a single GraphQL request could perform 100 operations or more via aliasing.

Strategies to prevent rate limit bypass include:

  • Per-operation limits
  • Restricting the number of aliases
  • Complexity analysis of incoming queries
  • Query depth limits

Properly Securing GraphQL APIs

Here are some best practices to follow when securing GraphQL APIs:

Authentication and Authorization

  • Implement proper authentication like JWTs to identify API consumers.
  • Leverage role-based access control to restrict permissions.
  • Enforce authorization in resolvers, schemas, and query analyzers.
  • Restrict Introspection in production environments.

Input Handling

  • Sanitize and encode user input as early as possible.
  • Validate user input meets requirements.
  • Use GraphQL context for passing input to resolvers.
  • Set whitelists for special fields like URLs and IPs.

Query Complexity Limits

  • Set maximum query depth allowed.
  • Restrict number of fields and aliases per request.
  • Implement computational complexity thresholds.
  • Enable automatic query cost analysis.

Rate Limiting

  • Enforce rate limits per operation rather than per request.
  • Restrict number of unique aliases.
  • Set maximum bytes per request.

Logging and Monitoring

  • Log key metrics like latency, errors, and traffic.
  • Use tracing to monitorresolver execution and performance.
  • Set alerts for unexpected errors and traffic spikes.
  • Perform regular security testing and audits.

Additional Security Tips

  • Follow the principle of least privilege in schema design.
  • Disable GraphQL playground in production.
  • Validate content types on requests.
  • Implement CSRF tokens.
  • Enable CORS if needed.
  • Mask errors to not expose internals.
  • Follow security best practices for underlying frameworks.

Conclusion

GraphQL provides a flexible and efficient alternative to REST APIs. However, unique security considerations need to be taken into account when designing, implementing, and securing GraphQL APIs. Common vulnerabilities like broken authentication/authorization, injection attacks, denial of service, and information leaks can put API consumers and backend systems at risk.

By following security best practices around authentication, access controls, query complexity management, input validation, and rate limiting, companies can securely take advantage of the benefits of GraphQL APIs. Proper tooling and processes for monitoring, logging, and testing also need to be implemented. With proper precautions taken by development teams, GraphQL can be safely leveraged to provide next-generation API capabilities.

Share this Doc

GraphQL API vulnerabilities

Or copy link

CONTENTS