Introduction to GraphQL API and its vulnerabilities
What is GraphQL?
GraphQL is an API query language that is designed to facilitate efficient communication between clients and servers. It enables the user to specify exactly what data they want in the response, helping to avoid the large response objects and multiple calls that can sometimes be seen with REST APIs.
GraphQL services define a contract through which a client can communicate with a server. The client doesn’t need to know where the data resides. Instead, clients send queries to a GraphQL server, which fetches data from the relevant places.
GraphQL vulnerabilities generally arise due to implementation and design flaws. For example, the introspection feature may be left active, enabling attackers to query the API in order to glean information about its schema.
GraphQL attacks usually take the form of malicious requests that can enable an attacker to obtain data or perform unauthorized actions. These attacks can have a severe impact, especially if the user is able to gain admin privileges by manipulating queries or executing a CSRF exploit. Vulnerable GraphQL APIs can also lead to information disclosure issues.
GraphQL schemas define the structure of the service’s data, listing the available objects (known as types), fields, and relationships.
The data described by a GraphQL schema can be manipulated using three types of operation:
- Queries fetch data.
- Mutations add, change, or remove data.
- Subscriptions are similar to queries, but set up a permanent connection by which a server can proactively push data to a client in the specified format.
Common Endpoint Names for GraphQL
GraphQL services often use similar endpoint suffixes. When testing for GraphQL endpoints, you should look to send universal queries to the following locations:
/graphql
/api
/api/graphql
/graphql/api
/graphql/graphql
If these common endpoints don’t return a GraphQL response, you could also try appending /v1 to the path.
Preventing GraphQL attacks
To prevent many common GraphQL attacks, take the following steps when you deploy your API to production:
If your API is not intended for use by the general public, disable introspection on it. This makes it harder for an attacker to gain information about how the API works, and reduces the risk of unwanted information disclosure.
For information on how to disable introspection in the Apollo GraphQL platform, see this blog post.
If your API is intended for use by the general public then you will likely need to leave introspection enabled. However, you should review the API’s schema to make sure that it does not expose unintended fields to the public.
Make sure that suggestions are disabled. This prevents attackers from being able to use Clairvoyance or similar tools to glean information about the underlying schema.
You cannot disable suggestions directly in Apollo. See this GitHub thread for a workaround.
Make sure that your API’s schema does not expose any private user fields, such as email addresses or user IDs.
Preventing CSRF over GraphQL
To defend against GraphQL CSRF vulnerabilities specifically, make sure of the following when designing your API:
- Your API only accepts queries over JSON-encoded POST.
- The API validates that content provided matches the supplied content type.
- The API has a secure CSRF token mechanism.
Preventing GraphQL brute force attacks
It is sometimes possible to bypass standard rate limiting when using GraphQL APIs.
With this in mind, there are design steps that you can take to defend your API against brute force attacks. This generally involves restricting the complexity of queries accepted by the API, and reducing the opportunity for attackers to execute denial-of-service (DoS) attacks.
To defend against brute force attacks:
- Limit the query depth of your API’s queries. The term “query depth” refers to the number of levels of nesting within a query. Heavily-nested queries can have significant performance implications, and can potentially provide an opportunity for DoS attacks if they are accepted. By limiting the query depth your API accepts, you can reduce the chances of this happening.
- Configure operation limits. Operation limits enable you to configure the maximum number of unique fields, aliases, and root fields that your API can accept.
- Configure the maximum amount of bytes a query can contain.
- Consider implementing cost analysis on your API. Cost analysis is a process whereby a library application identifies the resource cost associated with running queries as they are received. If a query would be too computationally complex to run, the API drops it.