Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you prevent nested attack on GraphQL/Apollo server?

How do you prevent a nested attack against an Apollo server with a query such as:

{   authors {     firstName     posts {       title       author {         firstName         posts{           title           author {             firstName             posts {               title               [n author]                 [n post]             }           }         }       }     }   } } 

In other words, how can you limit the number of recursions being submitted in a query? This could be a potential server vulnerability.

like image 352
jboothe Avatar asked May 20 '16 03:05

jboothe


People also ask

How do you implement security in GraphQL?

Use a whitelist for allowed characters. Define GraphQL schemas for mutations input. Use a single internal character encoding format to properly handle Unicode input. Add pagination to limit the amount of information that can be accessed by a single request.

What are key args in Apollo GraphQL for?

keyArgs function (advanced) You can define a completely different format for a field's storage key by providing a custom function to keyArgs . This function takes the field's arguments and other context as parameters, and it can return any string to use as the storage key (or a dynamically-generated keyArgs array).

How is error handling done in GraphQL?

The standard error handling mechanism from GraphQL with return a JSON containing: a data key which contains the data corresponding to the GraphQL operation (query, mutation, subscription) invoked and. an errors key which contains an array of errors returned by the server, with a message and location.


2 Answers

As of the time of writing, there isn't a built-in feature in GraphQL-JS or Apollo Server to handle this concern, but it's something that should definitely have a simple solution as GraphQL becomes more popular. This concern can be addressed with several approaches at several levels of the stack, and should also always be combined with rate limiting, so that people can't send too many queries to your server (this is a potential issue with REST as well).

I'll just list all of the different methods I can think of, and I'll try to keep this answer up to date as these solutions are implemented in various GraphQL servers. Some of them are quite simple, and some are more complex.

  1. Query validation: In every GraphQL server, the first step to running a query is validation - this is where the server tries to determine if there are any serious errors in the query, so that we can avoid using actual server resources if we can find that there is some syntax error or invalid argument up front. GraphQL-JS comes with a selection of default rules that follow a format pretty similar to ESLint. Just like there is a rule to detect infinite cycles in fragments, one could write a validation rule to detect queries with too much nesting and reject them at the validation stage.
  2. Query timeout: If it's not possible to detect that a query will be too resource-intensive statically (perhaps even shallow queries can be very expensive!), then we can simply add a timeout to the query execution. This has a few benefits: (1) it's a hard limit that's not too hard to reason about, and (2) this will also help with situations where one of the backends takes unreasonably long to respond. In many cases, a user of your app would prefer a missing field over waiting 10+ seconds to get a response.
  3. Query whitelisting: This is probably the most involved method, but you could compile a list of allowed queries ahead of time, and check any incoming queries against that list. If your queries are totally static (you don't do any dynamic query generation on the client with something like Relay) this is the most reliable approach. You could use an automated tool to pull query strings out of your apps when they are deployed, so that in development you write whatever queries you want but in production only the ones you want are let through. Another benefit of this approach is that you can skip query validation entirely, since you know that all possible queries are valid already. For more benefits of static queries and whitelisting, read this post: https://dev-blog.apollodata.com/5-benefits-of-static-graphql-queries-b7fa90b0b69a
  4. Query cost limiting: (Added in an edit) Similar to query timeouts, you can assign a cost to different operations during query execution, for example a database query, and limit the total cost the client is able to use per query. This can be combined with limiting the maximum parallelism of a single query, so that you can prevent the client from sending something that initiates thousands of parallel requests to your backend.

(1) and (2) in particular are probably something every GraphQL server should have by default, especially since many new developers might not be aware of these concerns. (3) will only work for certain kinds of apps, but might be a good choice when there are very strict performance or security requirements.

like image 181
stubailo Avatar answered Oct 07 '22 01:10

stubailo


To supplement point (4) in stubailo's answer, here are some Node.js implementations that impose cost and depth bounds on incoming GraphQL documents.

  • graphql-depth-limit
  • graphql-validation-complexity
  • graphql-query-complexity

These are custom rules that supplement the validation phase.

like image 31
Andy Carlson Avatar answered Oct 07 '22 00:10

Andy Carlson