Fundamentals of GraphQL-specific attacks

GraphQL vs REST APIs

Developers are constantly exploring new technologies that can improve the performance, flexibility, and usability of applications. GraphQL is one such technology that has gained significant attention for its ability to fetch data efficiently. Unlike the traditional REST API, which requires multiple round trips to the server to gather various pieces of data, GraphQL allows developers to retrieve all the needed data in a single request. This not only reduces the amount of data transferred over the network but also gives the client more control over the structure of the response.

Rest & GraphQL

However, with this powerful feature comes the risk of various attacks unique to GraphQL. Attackers can exploit the flexible nature of GraphQL queries to launch complex and sophisticated attacks, such as overwhelming the server with overly complex queries or extracting unauthorized data through crafted requests. This differs from REST APIs, where the endpoints are predefined, limiting the attacker’s ability to manipulate requests in such a granular way. Understanding these potential attacks and the unique aspects of GraphQL’s structure is crucial for developers to proactively safeguard their applications against malicious exploitation. Let’s explore the types of attacks that are specifically relevant to GraphQL and how they contrast with the security concerns traditionally associated with REST APIs.

Excessive Value Length

In a GraphQL API, an attacker might attempt to perform a Denial of Service (DoS) or cause other issues by exploiting how the server handles excessively large inputs. This type of attack is often referred to as an “Excessive Value Length” attack.
In this scenario, the attacker would send a mutation query or request with an excessively long string value for a variable or argument. The aim is to overwhelm the server’s resources, causing slow response times, high memory usage, or even a server crash.
For example, when making a POST request to a GraphQL endpoint using the application/json content type, the attacker might send a JSON object with a string variable that is several megabytes long or more. Here’s what this might look like:

POST /graphql HTTP/1.1
Content-Type: application/json
{
  "query": "mutation AddUser($username: String!) { addUser(username: $username) { id username } }",
  "variables": {
    "username": "A very long string... (extended to several megabytes in length)"
  }
}

If using application/graphql content type, the excessively long string value would have to be included directly in the query, as this content type does not support a separate variables field in the POST body like the application/json content type does. This could look like:

POST /graphql HTTP/1.1
Content-Type: application/graphql

mutation AddUser {
  addUser(username: "A very long string... (extended to several megabytes in length)") {
    id
    username
  }
}

In both cases, the attack’s effectiveness depends on how the server handles large input data. To mitigate such attacks, it’s essential to implement proper input validation, enforce limits on the total size of the HTTP request, the length of string inputs, and the complexity of the query.

Query depth

GraphQL queries can be nested, which allows clients to request complex data structures in one go. However, this flexibility can be exploited to create a deeply nested query that could potentially overwhelm the server, leading to a Denial of Service (DoS) attack.
Let’s take an example of an application that has three entities “User”, “Posts,“ and “Likes“ which are interconnected. A normal, benign query might look like this:

query NormalQuery {
  user(id: 1) {
    name
    posts {
      title
      content
      likes {
        user {
          name
        }
      }
    }
  }
}

In this case, the client is requesting the names of the user with id “1” and the names of all friends who liked his posts.
A malicious actor, however, might create a deeply nested query like this:

query DeeplyNestedQuery {
  user(id: 1) {
    name
    posts {
      title
      content
      likes {
        user {
          name
          posts {
            title
            content
            likes {
              user {
                name
                posts {
                  title
                  content
                  likes {
                    user {
                      name
                      # ... continue nesting as deep as you want
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

This query could potentially retrieve a huge amount of data and use up a lot of server resources, leading to a slowdown or even a DoS.
A well-configured GraphQL server should limit the maximum depth of queries to prevent such attacks. Additionally, a robust WAAP solution should detect such queries and prevent them from being executed. It’s important to implement measures like query depth limiting and complexity analysis to mitigate these kinds of threats.

Batching attacks

GraphQL has a feature where multiple queries (operations) can be batched together in a single HTTP-request. By combining multiple operations into a single request, attackers might organize batching attack and try to bypass security measures such as rate limiting.
Rate limiting is typically applied per HTTP request. If an attacker batches multiple queries or mutations into a single HTTP request, they might be able to perform more operations than allowed by the rate limit. This makes it easier for an attacker to carry out attacks such as brute-force, Denial of Service or data scraping.
Here’s a simple example with multiple login attempts in a single HTTP-request:

[
  {"query": "mutation { login(username: "user1", password: "pass1") { token } }"},
  {"query": "mutation { login(username: "user2", password: "pass2") { token } }"},
  {"query": "mutation { login(username: "user3", password: "pass3") { token } }"}
  //...continue adding as many operations as possible...
]

Maximum aliases

Aliases in GraphQL offer the capability to rename the result fields to prevent conflicts and enable better data organization. However, attackers can exploit this feature to launch a Resource Exhaustion or Denial of Service (DoS) attack. This attack aims to overwhelm the server with expensive requests, consequently causing significant server load and potentially making the server unavailable to legitimate users.
Here’s an example of how an attacker might use aliases to execute a DoS attack:

query HighlyExpensiveRequest {
    alias1: resourceExpensiveField
    alias2: resourceExpensiveField
    alias3: resourceExpensiveField
    ...
}

In this example, an attacker uses multiple aliases to request the same resource-intensive field multiple times, leading to a surge in server load.

To mitigate such attacks, it’s crucial to implement rate-limiting policies not just based on field names, but also considering the computational complexity of the queries. In addition, it’s advised to actively monitor the patterns of queries reaching your GraphQL server, employing anomaly detection mechanisms to identify and thwart suspicious activities.

Schema introspection

In GraphQL, an introspection attack can occur when an attacker leverages the introspection system to uncover details about the schema of the GraphQL API. By querying this system, they could potentially gain knowledge about all types, queries, mutations, and fields that are available in the API, which they could then use to construct more precise and damaging queries.
Below is an example of an introspection query that an attacker might use:

{
  __schema {
    types {
      name
      fields {
        name
        type {
          name
          kind
          ofType {
            name
            kind
          }
        }
      }
    }
  }
}

To protect against introspection attacks, consider disabling the introspection system in your production environment, or implementing authentication and authorization checks to ensure only trusted users can access it. It’s also a good practice to have field-level authorization to ensure that users can only access the data they are allowed to.

Debug Mode

When Debug mode is left turned on by developers, it allows attackers to gather precious information from excessive error reporting messages such as entire stack traces or tracebacks. Attacker can get access to the debug mode via “debug=1“ parameter in URI.

http://localhost/graphql?q=test_req&debug=1

GraphiQL

Besides Introspection itself, developers can also use GraphiQL. GraphiQL is the GraphQL IDE. The IDE can be reached for example under endpoints like /graphiql, /graphql/console or /__graphql. This makes the same information available as with Introspection, but in a nice and readable form.
GraphiQL should only be used in development or test environments that are not available to the public.

How to protect your GraphQL APIs

There are many different GraphQL servers that implement their own internal security measures. However, a challenge arises when the security team lacks direct access to the GraphQL server, inhibiting their ability to manage or influence the implementation of security policies based on internal mechanisms. Additionally, the process of coordinating changes in GraphQL server settings with developers can be time-consuming and inhibits immediate response to detected attacks.

The new GraphQL security policies in the Wallarm platform facilitate the implementation of fundamental security measures against GraphQL-specific attacks. These policies empower users to limit query complexity, effectively preventing undue resource consumption or performance degradation. Additionally, they enable restrictions on excessive information disclosure, thereby enhancing overall data security.

You can schedule a demo below to learn more about Wallarm App and API Security Platform: https://www.wallarm.com/request-demo.

The post Fundamentals of GraphQL-specific attacks appeared first on Wallarm.