WP GraphQL Vulnerabilities and Common Attacks: Seen in the Wild | Imperva

GraphQL Vulnerabilities and Common Attacks: Seen in the Wild

GraphQL Vulnerabilities and Common Attacks: Seen in the Wild

In our previous blog, we provided an overview of GraphQL security, along with details and examples of common attacks. Building on that foundation, this blog will take a closer look at real-world examples of GraphQL attacks that have recently occurred. We will explore the methods used by attackers to exploit vulnerabilities in GraphQL APIs and the consequences that these attacks can have on organizations. It is important to note that this blog will assume a basic understanding of GraphQL and its security considerations, so if you are new to this topic, we recommend starting with our earlier blog to get up to speed.

GraphQL statistics

Before delving into the nuances of these attacks, let’s ground ourselves with some data: Over a span of several months, we kept a close watch on approximately 6,000 endpoints using GraphQL traffic. The insights garnered are eye-opening. Stay tuned!

Figure 1 GraphIQ

GraphQL was developed, in part, to ensure concise response sizes suitable for mobile devices. We hypothesized that a majority of clients would be mobile-based, and our data supported this presumption: approximately 70% of the traffic stemmed from mobile devices. Out of the remaining traffic, 15% originated from automated tools, and a mere 5% came directly from browsers.

We have seen evidence of the use of different automation tools, among them CQ-API-Spyder, used for API attack surface discovery, and GraphQL Cop, which is a small Python utility to run common security tests against GraphQL APIs.

GraphQL HTTP servers should handle both the HTTP GET and POST methods. Among these, the POST method is more prevalent, and offers four options for the Content-Type header:

  • application/graphql: For sending raw GraphQL query strings.
  • application/x-www-form-urlencoded: For URL-encoded form data.
  • multipart/form-data: For file uploads.
  • application/json: For standard GraphQL requests and responses.

Of these choices, the latter, ‘Content-type: application/json’, emerged as the most frequently observed in our dataset.

GraphQL facilitates three distinct operations: query, mutation, and subscription. The most used among these is the ‘query’, primarily employed to retrieve data from the server. Our analysis revealed that nearly 90% of the traffic utilized this operation. The ‘mutation’ operation enables data modification on the server. Specifically, if there’s an intent to create, alter, or remove data via GraphQL, you’d employ the mutation operation. Lastly, the ‘subscription’ operation ensures a persistent, real-time server connection for continuous updates, making up a mere 3% of our findings.

While it is more common and practical to send the ‘query’ operation with a GET request, our data shows that POST was the preferred one, probably to avoid URL length limitations and to keep sensitive data out of the URLs.

True Threats: Genuine Attack Instances

Introspection Attack

GraphQL’s introspection system allows clients to query the API’s schema to discover available data types and their fields. While this feature is meant to be a convenience for legitimate users, it can potentially be exploited by attackers.

Attackers can abuse this feature to uncover detailed information about an API’s schema and, as a final step, gain access to sensitive data and execute unauthorized operations on the API.

We observed several introspection attack attempts on GraphQL APIs. The majority used the ‘__schema’ field to retrieve information about the API.  

Here is the most common attack we observed:

Figure 2 GraphIQFigure 1: Introspection example #1

This payload is very short and specific, making it an attractive first step for attackers seeking to determine whether introspection is activated on the target site. In addition, its small output size likely won’t trigger response size-based alerts, allowing the attacker to avoid immediate detection and remain under the radar.

When looking at one of the sessions, we observed the same IP address attempting to use this payload across various URLs. This suggests that the attacker is in the initial exploratory phase, which is known as the reconnaissance phase, actively searching to pinpoint the target’s GraphQL endpoint.

There were total of 99 URLs, here are some of them:

Figure 3 Sample URLs

There are many repositories that supply a list of possible GraphQL endpoints that attackers might use for this type of attack.

Another popular introspection query that we saw in our data aims to get as many details as possible about the victim’s schema: 

Figure 4 InspectionFigure 2: Introspection example #2

Both request and response are pretty long and might trigger an alert in the victim’s system.

We observed another vector related to introspection, where attackers used ‘schema’ instead of ‘__schema’ in introspection queries. The request looks pretty similar to the above example, but the attacker removed the ‘__’ prefix from the entire query.

It’s worth noting that the GraphQL specification requires the use of ‘__schema’ as the field name for the introspection query. Using ‘schema’ instead is technically non-standard, and may not be supported by all GraphQL clients or servers, but in some cases it will return the entire schema of the API (as shown in our previous blog).

Another noteworthy example is an array of two GraphQL queries, utilizing both ‘query’ and ‘mutation’ operations:

Figure 3 Introspection

Figure 3: Introspection example #3

In the first request, the attacker is trying to fetch the type of the root query object. By retrieving this information, the attacker will confirm whether introspection is enabled and potentially determine the primary type for querying data.

In the second request, the attacker is attempting to fetch the type of the root mutation object.

Knowing the primary type for mutations (actions that change data) can inform the attacker about the actions they might exploit to change data, create new records, or perform other disruptive actions.

Based on our research, 50% of GraphQL endpoints were targeted with introspection attacks, therefore we recommend disabling introspection in production environments unless it’s necessary.

Overall, while the queries in question seem simple, they can be the starting point for more complex and potentially malicious actions if introspection is left unrestricted.

GraphiQL

Just like the introspection attack, where attackers are trying to get details about your GraphQL schema, attackers may try to find the GraphiQL (GraphQL interface) endpoint of your API. 

By uncovering GraphiQL, attackers will get a friendly integrated development environment-like (IDE) interface to construct queries and see the result in real time. Thus it will give them a great advantage to get their hands on useful and meaningful information about your schema.

Since the GraphiQL can be located under different paths, attackers will perform an attack that iterates between paths until they find the right one. We saw this behavior from different IPs on different customers. In one of the examples, the same IP sent an HTTP request with the POST method to the following URLs:

  • /v1/graphiql.css
  • /v1/graphiql.min.js
  • /v2/altair
  • /v2/graphiql.css
  • /v2/graphiql.min.js
  • /v4/altair
  • /v4/graphiql.min.js
  • /v4/graphiql.php
  • /graphiql.min.js
  • /graphiql.php
  • /graphiql/finland
  • /graphql/console
  • /sphinx-graphiql
  • /v1/graphiql.js
  • /v1/graphiql.php

In these attacks, both post-body and parameter were empty.

In another example, the attacker was using some of the URLs from the above list, but this time with the GET method. The usage of an alternative method can be helpful, since introspection may only be disabled over POST.

Again, like in the introspection attack, there are many repositories that supply a list of possible GraphiQL paths that attackers might use to discover the right one.

DoS attacks

Directive Overloading

We noted another attack pattern in our data: directive overloading.

In GraphQL, directives are used to provide instructions to the server on how to execute a query. A directive can be used to modify the behavior of a field or fragment, and it can be used multiple times within a query.

In the example below we can see a request where the attacker used custom directives that probably do not even exist on the server.

Figure 5 Introspection URLs

Figure 4: Directive overloading

The purpose of this attack is to determine the feasibility of causing the server to consume a significant amount of resources while processing the query and generating the response, potentially leading to a denial-of-service (DoS) attack.

Furthermore, we encountered another attempt involving 1100 directives. We believe that the attacker sent requests containing a different number of directives, with the aim of overwhelming the server.

As a side note, this specific attacker was using a known GraphQL attacking tool called graphql-cop.

Another example involved a sequence of three requests where we could observe how the attacker’s requests evolved and escalated, affecting the server’s response.

The first request contained only 10 custom directives:

Figure 6 Introspection URLsFigure 5: The initial request in an escalating directive overload sequence

The second request included the string ‘@a @b @c @d @e @r @s @a @f @d @a’ repeated 100 times, resulting in 1000 directives. The server responded with an ‘Unknown directive “@a”‘ error, indicating the directive’s location in the request. This error occurred for each directive, increasing the response size and the time required to process the request. The third request was much larger, resulting in a response size that increased by a factor of three.

Another request combined directive overloading with reconnaissance of the GraphQL endpoint. In the same session, the attacker sent the same 10 custom directives, but each time with a different URL, attempting to identify a valid endpoint based on the server’s response.

Circular Fragments

Unlike circular queries, where the developer can prevent the attack by validating there are no circular relationships in the schema, when it comes to fragments the story is a bit different.

GraphQL fragments are essentially a set of fields that can be reused across multiple queries, mutations, or subscriptions. The use of fragments allows more complex queries to be built up from smaller, reusable pieces. They can also help to avoid redundancy in your queries, making your code easier to read and maintain.

Fragments are controlled only by the client which means the client can write whatever they want.

In the example below the attacker wanted to create an infinite-loop by calling fragment X that calls fragment Y and vice versa. This will cause an infinitely recursive request, which could potentially cause the server to crash or significantly degrade its performance! 

Figure 7 Introspection URLs

Figure 6: Cyclic fragments

This is just a single instance, but given that it’s solely controlled by the client, the possibilities are endless.

GraphQL servers are typically designed to detect such cycles and reject the query with an error message indicating a cyclic fragment reference, but there might be cases where the implementation isn’t spec-compliant.

Batch Queries

GraphQL supports query batching, which allows you to take a series of queries and send them in one shot. This means that one HTTP request contains several GraphQL queries, while the process in the backend is one after the other.

The example below, shows a request with an array-based batching, where the same query is being called several times:

Figure 7 Array Batching

Figure 7: Array-based batching 

The User-Agent of this request was escape-cli, which appears to be related to a GraphQL API discovery and testing tool. The same User-Agent was used in other exploitation attempts including SQLi, directory traversal etc.

False Alarms: Legitimate Yet Suspicious Requests

In the intricate world of GraphQL requests, appearances can be deceiving. In the following section we highlight two common examples of those GraphQL calls that may initially be considered to be an attack, but upon closer inspection, prove to be entirely harmless.

Circular Queries

In GraphQL, instances where types refer to one another can potentially lead to the creation of a circular query. Such queries can grow exponentially in complexity and size, and if left unchecked, they may escalate to a level that could severely load the server or even cause it to crash.

A circular query, also known as a cyclic query, is a type of GraphQL query where the requested fields include a reference back to the same or another related field, which can create a recursive pattern. This can potentially consume large amounts of resources, leading to a denial-of-service (DoS) attack.

In the example below, we see the ‘product’ field being queried with some additional fields inside the curly brackets. This structure repeats multiple times, creating a recursive pattern:

Figure 8 circular quiries

Figure 8: Circular queries

While it might be tricky to notice the recursive pattern at first glance, especially with multiple entries, it indeed exists. However, in this case, the request cannot cause an infinite recursive effect due to the way the schema is designed. There is a maximum limit on the number of related products that can be queried. If an attacker tries to exceed this limit, the query will not process additional levels, and the output will remain the same.

This is similar to the introspection query scenario, where attackers can request ‘ofType‘ a maximum of 7 times:

Figure 9 Introspection

Figure 9: Introspection recursive fragment

We didn’t see evidence of real attacks involving circular queries in our data yet. The likely reason for this is that an attacker needs to understand the victim’s schema to construct a working circular query. We assume that this type of attack requires prior knowledge of the victim’s server to be successfully conducted.

Developers can prevent circular queries by ensuring that the API schema does not contain relationships that can be traversed in a way that creates cycles. Additionally, another effective method to prevent this attack is to enforce depth limiting and query complexity restrictions to prevent excessive resource consumption.

Alias Overloading

One of the things we saw in the data is a request that uses multiple aliases as part of a query. 

 Alias is used to give a different name to a field in a query or mutation. This can be useful in cases where you want to request the same field multiple times with different arguments or to avoid naming conflicts when querying multiple fields that have the same name.

Here is a partial screenshot of the request:

Figure 10 graphiq png

Figure 10: Potential alias overloading attack

In the truncated example, there were at least fifteen different aliases to getMedicine, each time with a different id. 

The reason we suspected this request was an attack is due to the fact that attackers may use aliases for batching, known as alias overloading. Where one request will be sent over the network and then all queries will be executed sequentially.

When we inspected the traffic of this specific customer, we realized that this type of query is a common behavior in the API which exists on a daily basis.

Conclusion

As we navigate the complexities of GraphQL security, understanding both the actual risks and the potential false alarms is crucial. This knowledge not only bolsters our defense mechanisms but also ensures a smoother, more efficient, and secure GraphQL experience for all.

Ensuring the security of GraphQL endpoints is crucial for developers. Key protections include implementing rate limiting to prevent directive overloading, validating and sanitizing input to guard against injection attacks, and employing robust authentication and authorization mechanisms to control access. These measures help safeguard sensitive data and maintain the integrity of the server.

Imperva Web Application and API Protection (WAAP), which includes API Security capabilities, provides protection against conventional web application threats – including injection attacks – but also adds an extra layer of security with dedicated inspection and the ability to protect applications that use GraphQL APIs. Imperva also offers a set of GraphQL rules designed to address known threats, which customers can activate with the assistance of our Advanced Security Response (ASR) team.