Forging GraphQL Bombs, the 2022 version of Zip Bombs

Forging GraphQL Bombs, the 2022 version of Zip Bombs

Zip Bombs are a thing of the past, but the concept behind them is still relevant nowadays. Indeed, your GraphQL application might be vulnerable to what we'll call GraphQL Bombs in this article. Read on to know if you're vulnerable and how to secure your GraphQL application!

How do zip bombs work?

Before diving into the topic, let's take a moment to understand the concept behind zip bombs.

Zip files are lossless compressed archives whose most common compression algorithm is named deflate. It works by finding repeating patterns in the data, then replacing these patterns with a much shorter token.

A diagram reading "hello Alice hello Bob" on the first line, then compressed as "x Alice x Bob" on the second line.
Zip compression works by replacing repeating patterns with tokens. 

Therefore, a repetitive sequence of bytes will be much shorter after compression. Creating a zip bomb consists of meticulously crafting a sequence of bytes that compress very well, by several orders of magnitude. Then, when the victim decompresses the zip file, the resulting data will be much larger than the original archive.

Aliased queries

GraphQL is a powerful language with many not-well-known features. One of these features is the ability to alias queries.

Let's consider a simple blog with an article(id: Int!) query. If we were to fetch one article, we would do it like this:

query {
  article(id: 1) {
    title
    author
  }
}

This will produce something like this:

{
  "article": {
    "title": "Hello World!",
    "author": "John Doe"
  }
}

But GraphQL has a nice feature allowing developers to query the same resolver several times with a different return name:

query {
  first:  article(id: 1) { title author }
  second: article(id: 2) { title author }
  third:  article(id: 3) { title author }
}

This will produce a similar result, but article is now aliased as first, second and third:

{
  "first": {
    "title": "Hello World!",
    "author": "John Doe"
  },
  "second": {
    "title": "Yay, second article!",
    "author": "Jane Doe"
  },
  "third": {
    "title": "That's a lot of articles",
    "author": "Jaune D'œuf"
  }
}

You may already notice something there, and indeed, you can already design a first vulnerability to exploit this feature – aliasing facilitates brute-force:

mutation {
  a1: login(user: "john", password: "password") { id }
  a2: login(user: "john", password: "qwerty") { id }
  a3: login(user: "john", password: "123456") { id }
  # Let's try the most common passwords!
}

File uploads

The second part of the vulnerability requires having enabled file uploads over GraphQL.

Many popular GraphQL engines support uploads in GraphQL, some even natively.

The GraphQL multipart specification describes how to implement file uploads in GraphQL. While usual GraphQL queries are sent as application/json, file uploads are sent as multipart/form-data. This means that the HTTP request body has multiple parts, and their functions, described in the specification, can be summarized as follows:

  1. The operations part contains the GraphQL query. This is the part that is usually sent as application/json.
  2. The map part helps the server to find data in the request body.
  3. Any other part can be used in the operations part as long as it is correctly mapped. These parts may contain any type of data the server can handle but are usually images or binary data.

Here is what a profile picture upload looks like:

POST /graphql HTTP/1.1
Connection: keep-alive
Content-Length: 78346
Content-Type: multipart/form-data; boundary=----boundaryMGv2RzA6GpOE3Hry
Host: example.com

------boundaryMGv2RzA6GpOE3Hry
Content-Disposition: form-data; name="operations"

{
  "query": "mutation ($picture: File!) {updateUserPicture(picture: $picture)}",
  "variables": { "picture": null }
}
------boundaryMGv2RzA6GpOE3Hry
Content-Disposition: form-data; name="map"

{
  "file1": ["variables.picture"]
}
------boundaryMGv2RzA6GpOE3Hry
Content-Disposition: form-data; name="file1"; filename="gautier.jpg"
Content-Type: image/jpeg

(77 kB of binary data)
------boundaryMGv2RzA6GpOE3Hry--

You can see here that the part having name="file1" is mapped to variables.picture, allowing the server to find the file in the request body.

GraphQL file uploads work almost the same as file uploads in REST, at least from the HTTP perspective, which means that vulnerabilities that exist in REST can be exploited in GraphQL. But, unfortunately, that's not all…

GraphQL bombs

As you might have guessed, GraphQL bombs combine the two previous features introduced in the article. The concept is the following: referencing the same file several times using aliased queries.

Let's consider that we call updateUserPicture(picture: File!) one thousand times, using aliases, with all calls referencing the same 1 MB file:

mutation {
  a1: updateUserPicture(picture: $picture)
  a2: updateUserPicture(picture: $picture)
  a3: updateUserPicture(picture: $picture)
  # ...
  a1000: updateUserPicture(picture: $picture)
}
A diagram showing a single file mapped to four different GraphQL requests.
GraphQL bombs consist in referencing the same file several times.

This request would be less than 2 MB but would result in 1 GB of data for the server to process.

Depending on what the server does with the data, this request may cause memory or CPU exhaustion, leading to decreased performance or even a server crash.

Is my application vulnerable to GraphQL Bombs?

This is a question you should ask yourself… but figuring out the answer remains challenging! Indeed, GraphQL is new and lacks just lacked the proper tooling, many development teams are just skipping security...

That's why at Escape, we created the first GraphQL Security scanner that allows developers to find and fix vulnerabilities in GraphQL applications during the development lifecycle before they even reach production!

Escape

It understands the business logic of GraphQL APIs and looks for more than 50 kinds of vulnerabilities so that you never have to worry again about:

  • Resolver performance (GraphQL bombs, N+1 issues, cyclic queries, query complexity DOS…)
  • Tenant isolation (access control, data segregation between users…)
  • Sensitive data leaks (personally identifiable information, tokens, stack traces, secrets…)
  • Injections (SQL, NoSQL, XSS…) and requests forgery
  • … and more than 50+ advanced security issues!

We constantly update our engine with state-of-the-art GraphQL Security research so that you never have to worry about the security of your GraphQL application again!

Start monitoring the security of your endpoints today with a 7-day free trial.
Get results in one minuteno credit card required.

Mitigation

There are several steps necessary to mitigate this vulnerability:

  1. Properly configure the server limits to file uploads.
  1. Limit the usage of batching and aliasing with GraphQL Armor, an open-source project developed by Escape - GraphQL Security to address the most common GraphQL vulnerabilities.
  2. If GraphQL Armor does not support your engine yet, you may also try graphql-no-batched-queries and graphql-no-alias.
  3. Wanna know more about automated GraphQL security testing? Read our blog article "How to test your GraphQL API?".