Safety
GraphQL gives clients a lot of flexibility to make queries, but it can also be dangerous if not used properly.
Query Depth
GraphQL queries can be nested arbitrarily deep. This can be a problem if an attacker is able to craft a query that is very deep and causes your server to work very hard to resolve it.
For example, supposed we implemented a list of episode for a podcast, we could allow queries like this:
{
podcasts(first: 50) {
edges {
node {
title
episodes(first: 50) {
title
}
}
}
}
}
which is fine on its own, but we also allowed clients to fetch the podcast on the episode node, so we could also run the following query:
{
podcasts(first: 50) {
edges {
node {
title
episodes(first: 50) {
podcast {
episodes(first: 50) {
podcast {
episodes(first: 50) {
podcast {
title
# ...
}
}
}
}
}
}
}
}
}
}
This query is very deep and could cause our server to work very hard to resolve it, which could lead to a denial of service attack.
To prevent this, we can limit the depth of the query by using the
QueryDepthLimiter
extension provided by Strawberry:
from strawberry.extensions.query_depth_limiter import QueryDepthLimiter
...
schema = strawberry.Schema(
query=Query,
mutation=Mutation,
extensions=[
# 8 is the maximum depth in this case
QueryDepthLimiter(8),
],
)
Query Complexity
Another approach to prevent queries that are too complex is to limit the complexity of the query. This is similar to the query depth, but instead of limiting the depth of the query, we limit the complexity of the query.
For example, if we have a query like this:
{
podcasts(first: 50) {
edges {
node {
title
episodes(first: 50) {
title
}
}
}
}
}
we can assign a complexity to the fields that can be paginated and that complexity will be equal to the number of items requested, times the complexity of parent.
For example in this case, the complexity of the episodes
field would be 50,
and the complexity of the podcasts
field would be 50 * 50 = 2500. So the
total complexity of the query would be 2550.
{
# complexity: 50
podcasts(first: 50) {
edges {
node {
title
# complexity: 50 * 50 = 2500
episodes(first: 50) {
title
}
}
}
}
}
Unfortunately this is not yet built into Strawberry, but it can be implemented creating an Extension.