GraphQL is one of the hottest topics in the API world right now. It provides an abstraction layer over more traditional HTTP communications, and has changed the way we build web applications by providing us with modern and easy-to-use tooling.
As with the addition of any new technology, no matter its impact, it is important to ask whether it introduces or prevents security issues, and how to handle those. I recently gave a talk on GraphQL security at APIdays Paris 2020, and I wanted to take a bit of time to expand on the topic.
In this article we will take a look at how usual web application security considerations apply or don’t apply to GraphQL.
String injections over GraphQL
String injections happen when a malicious attacker is able to tamper with the logic of an application by injecting a string that will be interpreted as executable code. Such string can be interpreted by a database engine (SQL injections), by the runtime itself (Remote Code Execution) or even by web browsers (XSS).
GraphQL comes with some scalar types out of the box. It comes as no surprise, but any field with a
String type is a potential candidate for a string injection. Also, it is worth noting that the
ID default scalar is serialized in the same way as a
String, making it a good candidate for injections as well.
Let’s take the following GraphQL schema:
Which uses the following resolver:
Let’s call this resolver with a legitimate request:
This seems to work well, however, since the SQL query in the resolver is using string concatenation, we can try an SQL injection:
As we can see, the SQL injection was successful. To fix it, the resolver’s code needs to be updated to use prepared statements.
Other String-based injections such as Remote Code Execution or XSS will be possible too. Basically, GraphQL does not enforce any validation on Strings that could prevent the usual injections. This means that you will have to sanitize user input in GraphQL as you would have done in a non-GraphQL application.
Object injections over GraphQL
Object injections can lead to attacks based on deserialization or NoSQL injections. The default scalars of the GraphQL language technically prevent the client from injecting arbitrary objects through a GraphQL query.
So is that the end of the story with object injections? Not really. Some applications rely on extra scalars definitions. These can be custom built or imported through a library.
When using custom scalars, you could also open the door to insecure deserialization attacks. Once again, you should follow the usual protection methods around object injections when building a GraphQL application.
Can the GraphQL schema be kept private?
The question of whether or not to keep the GraphQL schema of an application private is a recurrent one. One could easily consider that disclosing the whole schema publicly can help an attacker identify potential injection points or sinks for GraphQL malicious queries that could lead to a Denial of Service. That would be a legitimate claim, but in some cases, in such as with public APIs, disclosing the schema is essentially the best way to document it to your users.
Even if you don’t disclose the schema, a web application is made to be used by clients. It would not take long for an attacker to rebuild the schema of an application by auditing the frontend code.
To identify the schema of a running application, there are several methods.
The first way of identifying the schema of a running application is to use the introspection feature of GraphQL. These features are actually used by GraphQL playgrounds to identify the schema of the application and provide features such as autocompletion or frontend-side rich query validation.
If this feature is not needed, it is probably still a good call to disable introspection in production, however, If introspection is disabled, it is still possible to identify part of the schema by auditing the available clients.
One simple solution is to play with the application and observe the network communication (for instance, in the network tab of the browser developer’s tools).
Alternatively, identifying the client libraries used to interact with GraphQL will help identify the schema. For instance, identifying the calls to the graphql-tag library in a website could easily lead to reversing the part of the schema that are used in the application.
What about authorization?
In a REST world, it is standard to implement role-based authorization per HTTP endpoint. In the case of a GraphQL application, there is only one HTTP endpoint: the
This is actually one of the most interesting points of switching from say REST to GraphQL: most of the logic that used to be HTTP-endpoint-based becomes resolver-based.
As a result, it is tempting to consider that resolvers have a 1:1 mapping with data entities in a database. However, this mindset would lead to building revolvers that are not context-aware and would fail to enforce authorization (and maybe authentication) concerns in their logic.
Once again, the conclusion is that the usual security best practices for web applications still apply. The resolver has replaced the controller, but the stakes have not changed: you need to check the request context in the business logic to ensure that the user has sufficient right to perform the requested operations.
Supporting GraphQL in Sqreen
On December 16th, 2020, Sqreen released version 1.57.0 of its Node.js agent with coverage for GraphQL. From this point on, you can benefit from the deep app sec visibility and protection capabilities of the Sqreen RASP and In-App WAF for any Node.js application using the
graphql npm package (which is used by
express-graphql and Apollo GraphQL libraries). We will soon release support for GraphQL in the other technologies supported by Sqreen. You can check it out by updating your Node.js agent to version 1.57.0 or with a free trial.
In this article, we covered GraphQL security in light of the question “What has GraphQL changed in terms of web application security?” We did not cover the case of complex GraphQL queries leading to a potential Denial of Service, but there is already extensive literature on the topic.
At the end of the day, GraphQL applications have the same security surface as other web applications. The sinks for attacks have another form, but the potential vulnerabilities are still there and need to be covered.