Hacker News new | ask | show | jobs
by devit 3935 days ago
How do GraphQL implementations avoid denial of service?

In other words, what stops anyone from easily disabling a website by making a few parallel extremely complex GraphQL requests that consume all CPU and I/O, and perhaps result in holding some locks for a very long time?

In normal APIs you can make sure most endpoints are cheap to run, and throttle, secure or otherwise control the ones that must be expensive, but that doesn't work if you expose a flexible layer like GraphQL (or SQL).

5 comments

FB's GraphQL APIs are only used by our first-party applications; for third-party APIs (where you don't control the callers), it definitely gets trickier for the reasons you note. One option would be to do some analysis of the query in advance (effectively, assign each field a "cost"), and reject queries that have too high of a cost. The "cost" metric could basically be "around how many objects will this return", so in the

  user {friends {friends {friends {friends{id}} } } }
case (which is the canonical example in FB's schema of a crazy query), we would note that there's a 5000 friend limit, and so that query would potentially query 5000^4 = 6.25e14 nodes, and based on that we would (hopefully) reject it.
This is a concern even for first-party apps, as you are not secure from a malevolant client. Or hell, your own client could have bugs which create some insanely expensive queries on, say, 1% of your devices - didn't catch it in QA, end up pushing it to millions of device for a nice ddos.
Surely you know that anyone can access the binary code of your first-party applications (using a jailbroken iPhone and a rooted Android device), decompile it (using jd-gui, the Hex-Rays ARM decompiler, etc.) and arbitrarily use the APIs they expose, right?
The Youtube API has a concept of quotas, where requesting some types of information is more costly than others. I imagine you could implement a similar system that additively increases the cost of a query as it grows in complexity and fails if the quota is exceeded.
It's the same here. Limit number of results to 10 by default (for example) and allow query to specify number if you need more. Then limit the number to a certain amount
There are more ways in GraphQL to create huge result sets than that though.

For example, a query like "user {moviesWatchedByUser {usersWhoWatchedMovie {moviesWatchedByUser {usersWhoWatchedMovie ..." is allowed by GraphQL and will generate output with size exponential in the input size.

You can also do "{a1: expensiveOperation, a2: expensiveOperation, a3: expensiveOperation, ..." and trigger expensiveOperation an arbitrary number of times (for each item in the list you apply that to).

By using a sequence of fragments that include the next fragment more than once, it looks like you can trigger expensiveOperation an exponential number of times.

It's not clear if there is a way to prevent all this without severely impacting usabilty (by warping the schema design and adding GraphQL limits to handle this) or reliability (by enforcing hardcoded low resource usage limits).

Personally I'd probably enforce this at the level of whatever services the GraphQL layer is calling, assuming you're using it as an aggregation layer for lower level services within your organisation.

Otherwise, it should be possible to apply throttling to (for example) expensiveOperation in the same way that you would a RESTful API at the moment.

You have to write a functionality for this scenario. Similarly You can also ask how Sql server is going to avoid DOS attacks?
Perhaps you just analyze the queries and deny the ones that look too expensive?