GraphQL is a query language for APIs. It provides a complete and understandable description of the data in API and gives clients the power to ask for exactly what they need, and nothing more. It is used by APIs to access data from the database through a single endpoint and it makes this possible by using a defined schema that specifies exactly what data we want to access. GraphQL Security is important as many big companies nowadays are using it for requesting the API to query the data, delete, update, and many more actions.
GraphQL vs REST APIs
This is different from REST APIs where there are separate requests for each action or resource. In graphQL, we have actions to which we can specify what data we need specifically and there is no need for separate endpoints to get data from a single source. While in REST APIs, we have separate endpoints for each of the data resource. Due to this, many applications are now using graphQL for ease and flexibility as well. This makes GraphQL security an important focus area to look upon to make applications protected.
GraphQL Operations
There are 2 most common types of operations in GraphQL as described below
- Query
- This is used to read data only
- Mutation
- This is used to modify the data
Query
It queries the graphQL action to fetch the specific data from the source. The sample for query operation is as follows
query Users{
user{
name
email
contact
}
}
- The query specifies the operation type (Query in this case)
- Users is the operation name given to us
- Everything enclosed in the operation is the fields that specify exactly which data we need
Introspection Query
Whenever we want to know what kind of query graphQL supports, we can use the introspection system. This system gives us the details about what kinds of queries, types, and fields are supported by graphQL. By default, we can use this system on every API that uses graphQL.
Apollo graphQL disables introspection.
Introspective queries always start with two underscores __
. Following are some common introspective queries that we can use to get information from graphQL
- __Schema is the primary source and enables us to fetch the whole graphQL schema
- types tell us which types the schema has
- __type where we specifically examine a particular type using an argument
- queryType tells us what are the available query operations in the schema
GraphQL security checklist contains many vulnerabilities to test for but here we will demonstrate some common and basic ones.
Introspection Lab
The lab for demonstration purposes can be cloned from GitHub. The steps to make the lab work are simple.
- Clone the git
- Go to lab1-info-introspection
- Install the required modules using
pip3 install -r requirements.txt
- Populate the graphQL database using
python3 populate-database.py
- Start the application using
python3 app.py
- Open the browser and navigate to
http://localhost:5000
You will be able to see the following working application screen

Fire up the burp suite and refresh the page to route the request through burp and you’ll see the graphQL request as shown below

Send this request to the repeater to send multiple requests and play around with it.
In the repeater, you can see the response by the graphql endpoint as below

Let’s understand the query
{
"query":"{allPosts {edges {node {title body author { username }}}}}"
}
It is querying to get data from the allPosts and then we have the edges containing the node. Nodes contain the data that we want so in this case, we want to get the title, body, and username of the author (nested structure).
💡 Nodes are separate entities and edges connecting these nodes.
We now have the graphql endpoint and navigating to the endpoint in the browser will sometimes show the browser user interface for it as below

There we can put our queries directly and fetch data. Here we will now use the introspective query to get the schema and types as below

We can query the specific type to see what fields it has and later get the data. For that, we can modify the query as below
query Introspection {
__type (name: "UserObject"){
fields{
name
}
}
}

IDOR in GraphQL
Similar to the last lab, run the IDOR lab and you will see the login screen asking for the credentials.

We don’t know the credentials but if we go back to the lab folder, we can see data.sqlite file which might contain what we are looking for. Open the file using sqlite3 and run queries to get creds as below

First we run the query to find the available table names in the database and then select all records from the users table to get the credentials.
Now we have the credentials, we log in with johndoe
user with password password1
The request is the same as in the previous lab but with some additional headers for API key and uuid as below

After directory brute force, we get the settings endpoint which requests the graphql endpoint on the singleUser action and gives the API key as below

Looking at the graphQL query it is difficult to read it. For ease, we use the inQL extension in burp which parses the query and gives a neat view as below

Here we can change the user argument to some other value to pass to the singleUser action and see if it returns anything. We change it to 2 in our request and we get the API key for Jim Carry and we have an IDOR here.

Injection in GraphQL
Run the injection lab and go to the graphql endpoint in the browser. From the user interface, we can query for available operation types as below

Here we have the getUser operation. We try to get some users by providing the argument of a username and passing the dummy value to it

The query results show that Jawad user is not in graphQL database. But since we want to do the injection, we can try the simple injection testing query to put in the username below

We can see that this action is vulnerable to SQL injection that can be exploited further with different payloads. This could lead to gaining admin rights, bypassing authentication and many more.
Leave a Reply