GraphQL has revolutionized the way APIs are designed and consumed, offering flexibility and efficiency in data querying. Since Facebook introduced it in 2012, it has acquired, but so far held its niche market share within APIs. As APIs exploded, so has GraphQL with them.
However, GraphQL's flexibility also introduces several security challenges. Common security issues include injection attacks, unauthorized access, denial of service (DoS) attacks (complex query handling), excessive data exposure.
When thinking of GraphQL, most developers think of exposing a relational database via an API. Whether the data is actually stored in a relational database, does not matter for the API. What matters are the relations between entity types as well as the properties of entities.
You can formulate a GraphQL query
{
products {
name
vendor {
name
}
}
}
to retrieve a list of products (with their names as well as their vendor's names).
The data might be stored in two relational tables (called products
and vendors
, linked by a
foreign key), or in a totally different way: GraphQL just describes the query syntax.
GraphQL is often preferred in architectures where
SQL
queries would actually lock the API provider into actually using a relational database with SQL support,While the mental model described in the previous paragraph is correct (and standardized in the last GraphQL specification to date, the "October 2021 Edition"), it does not cover the the whole picture:
Using nested queries
{
products {
name
vendor {
name
products {
name
vendor {
name
}
}
}
}
}
(This queries all products, their vendors as well as, for each vendor, the list of their products and again their vendors) the amount of data returned, when served correctly, grows exponentially with the query's nesting depth. Essentially, this is the definiton of a Denial of Service attack surface: As small request leads to huge resource consumption (CPU and memory, in this case).
While most users of GraphQL think of the query
s described above, GraphQL also supports
mutation
s (allowing the caller to modify data) as well as subscription
s (allowing the caller
to be notified when data changes).
GraphQL can therefore easily open your data to unwanted modifications.
While GraphQL is standardized and has not changed much in the latest specification changes (last specification release already 4 years old), the technology of transferring GraphQL documents from the caller to your server has not:
GraphQL-over-HTTP has no releases yet, is described as "Stage 2: Draft" and the last commits are just a week old, as of writing.
extension
s, whose functionality - and therefore also attack surface - is, by design, completely
unknown.
<spring:beans xmlns="http://membrane-soa.org/proxies/1/"
xmlns:spring="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://membrane-soa.org/proxies/1/ http://membrane-soa.org/schemas/proxies-1.xsd">
<router>
<request>
<graphQLProtection maxRecursion="2" />
</request>
<target url="https://www.predic8.de/fruit-shop-graphql" />
</router>
</spring:beans>
More fine-grained configuration options are also available.
Attacks using Injection or Cross Site Scripting usually rely on using special characters like single quotes ('
) or left angle brackets (<
).
We advise combining Membrane API gateway with a Web Application Firewall (WAF) like Snort to protect against these types of attack. Membrane will take care of validating the general GraphQL syntax and/or blocking specific GraphQL-related features in this scenario.
Rate Limiting might also help reduce the exploitability of existing weaknesses so far, that they become irrelevant (e.g. guessing the correct password/access key).