Some of your users start complaining that your web application is not secure and that someone managed to log into their accounts… You look at the user history and network activity, but nothing looks wrong. However, you notice that your server is slower than usual.
Two days later, your application is down due to a DoS attack, i.e. too many requests crashed your service, and it is not available anymore. But your network activity seems pretty quiet, and you have a firewall that should have prevented the DoS. That’s weird, isn’t it?
From GraphQL Aliases to Batch Attacks
It comes from a super useful feature of GraphQL: aliases.
What are GraphQL aliases?
When making a query using GraphQL, the response has the same format as the query. But what about querying the same field with different attributes? Without aliases, this isn’t possible as JSON field names must be unique. Here is the example from the GraphQL documentation:
In the above example, the two hero fields would have conflicted, but since we can alias them to different names, we can get both results in one request.
This feature allows multiple queries to be sent with a single GraphQL request. This technique is called batching. Only one batch will transit on the network and then all queries will be executed sequentially.
Batch attacks with aliases
It is an easy way to bypass attempt and rate limits. Indeed, limits are usually set up on the number of API calls, but what if a single API call can create 10,000 database requests?
Tools responsible for securing web applications like firewalls and rate limiters cannot detect abnormal activities: they only monitor API calls…
Therefore, all your common protection mechanisms will fail to detect and deny a brute-force attack. So, an attacker can simply make one API call with hundreds of attempts to find user credentials (email, passwords). They can also bypass two-factor authentication (2FA) by sending all the token variants of the one-time password (OTP).
An attacker could use a simple example to try different credentials.
login(username: "Tom", password: "password")
second: login(username: "Tom", password: "password123")
third: login(username: "Tom", password: "TomTheBest")
And this is the server response.
In this case, the third password was correct, and the attacker could now log in as a legitimate user with the authentication token.
Another consequence of this attack is Denial of Service (DoS). Indeed, if the attacker uses a huge number of aliases, your server will have a lot of work to do and may be unable to handle it. This causes a slowdown or even a complete shutdown, i.e. your server becomes unresponsive (DoS). And again, your firewall can’t prevent this.
Is my application vulnerable to Denial of Service?
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!
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 (DOS, GraphQL bombs, N+1 issues, cyclic queries, query complexity, aliasing and batching limits…)
- 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 minute – no credit card required.
Protecting your GraphQL API against Batch Attacks
The alias functionality cannot be directly disabled (at least not in the current implementation), and as it is a useful feature, it will not be the best option.
Instead, you must implement a validation process to ensure the query doesn’t contain more aliases than the chosen maximum. A good security practice is to deny by default. Here, it means denying aliases except when they are really useful. Therefore, in the configuration options, set the default value for query and mutation to 1 (i.e. '*': 1) and add custom values for specific fields if necessary.