Now we know what our Schema
looks like, and what queries and mutations we can do. Now let's learn how to craft out queries from the information we got from introspection which we discussed in Part 1 of this blog.
GraphQL Pentest Series
1️⃣ GraphQL Pentesting for Dummies! Part-1
2️⃣ GraphQL Pentesting for Dummies! Part-2
3️⃣ GraphQL Pentesting for Dummies! Part-3 [coming soon]
GraphQL Operations
• Query: fetching data using specifically defined query operations.
• Mutations: for modifying any data (creating, updating, or deleting) in a database using operations.
• Subscriptions: for receiving real-time messages from the back end.
While we use queries to fetch data, we use mutations to modify server-side data.
If queries are the GraphQL equivalent to GET
calls in REST, then mutations represent the state-changing methods in REST (like DELETE
, PUT
, PATCH
, etc).
For learning purposes, we will be using the publicly available Fruit Shop Graphql
https://www.predic8.de/fruit-shop-graphql?query=
First thing first, let's check if introspection queries are enabled to get the landscape of our Graphql API.
{__schema{queryType{name}mutationType{name}subscriptionType{name}types{...FullType}directives{name description locations args{...InputValue}}}}fragment FullType on __Type{kind name description fields(includeDeprecated:true){name description args{...InputValue}type{...TypeRef}isDeprecated deprecationReason}inputFields{...InputValue}interfaces{...TypeRef}enumValues(includeDeprecated:true){name description isDeprecated deprecationReason}possibleTypes{...TypeRef}}fragment InputValue on __InputValue{name description type{...TypeRef}defaultValue}fragment TypeRef on __Type{kind name ofType{kind name ofType{kind name ofType{kind name ofType{kind name ofType{kind name ofType{kind name ofType{kind name}}}}}}}}
Let's visualize it using any of the methods we discussed earlier. I am using my favorites graphql voyager
1. Queries
1.1. Return all Objects of a Type
The query below returns a list of products with their names. You can use the methods categories , customers , vendors and orders as well.
Fields: At its simplest, GraphQL is about asking for specific fields on objects. The field name
returns a String
type, in this case the name of the products
in the fruit shop.
To return a list of products with their names. We check the schema for the product and see what all fields can be queried from products.
We can see there are id, name, price, categoryID and vendorID.
Let's craft a query to return a list of products with their names. It will look something like this
query{products {
name
}
}
1.2. Searching by ID
Let's craft a Query returning product with id 7, we can use id
as an argument. You can use name, price, categoryID and vendorID anyother subfeilds as arguments.
Arguments: An argument is a value you provide for a particular field in your query. The schema defines the arguments that each of your fields accepts. In this example, we used the argument id
to get the product with argument value "1"
{
products(id: "1") {
name
}
}
why the value of id
is in quotes? because it's a string. refer to the schema
1.3. Selection of Subfields
The returned objects are JSON-objects that can be quite complex. The fields of an object can itself be a JSON-object with fields of its own and so on. In a query you can specify which of these fields and subfields you want to return. If a field has subfields you have to make a selection of these subfields in order for the query to work.
As we saw earlier there are other subfields other than name
in products, for example, let's check price
of id=2
.
{
products(id: "2") {
name
price
}
}
Let's craft a query returning category
and vendor
of a product
{
products(id: "2") {
name
price
category {
name
}
}
}
Let's craft a query returning category
and vendor
of a product
{
products(id: "2") {
name
price
category {
name
}
vendor {
name
}
}
}
Besides the id you can give query methods additional arguments. The methods then only return objects which have in their fields values corresponding to the values of those arguments.
1.4. Filtering Fields
Query returning products with price=1.1
and category=1
{
products(price: 1.1, categoryID: 1) {
name
price
category {
name
}
vendor {
name
}
}
}
1.5. Multiple Levels of nested Subfields
By selecting subfields you can build queries that have deep levels of subfield nesting. You can even include circles of subfields.
Query with nesting categories -> products -> vendor -> products
{
categories(id: "1") {
name
products {
name
vendor {
products {
name
}
}
}
}
}
Some more terms!
Aliases
We can't directly query for the same field with different arguments. That's why you need aliases - they let you rename the result of a field to anything you want.
{
Produc1:products(id:"1") {
name
}
Produc2:products(id:"2") {
name
}
}
Operation name
Until now, we have been using a shortened syntax that excludes both the query keyword and the query name. However, in production applications, it is beneficial to include these elements to increase the clarity of the code.
Here’s an example that includes the keyword query
as operation type and productNameandPrice
as operation name :
query productNameandPrice {
products(id: "1") {
name
price
}
}
Variables
So far, we have been writing all of our arguments inside the query string. In GraphQL, you can use variables to reuse the same query/mutations written by the client, with different arguments.
Variables can be declared after the query
or mutation
and are passed like arguments to a function and begin with $
.
query productNameandPrice($testid : String!) {
products(id:$testid){
name
}
}
Query Variables
{
"testid": 1
}
There are three steps that need to be done when you start using variables in your query or mutation:
- Replace the static value in the query with $variableName
- Declare $variableName as one of the variables accepted by the query
- Pass variableName: value in the separate, transport-specific (usually JSON) variables dictionary
Default variables: If default values are specified for all variables, you can execute the query without providing any variables. If any variables are passed as part of the variables dictionary, they will override the defaults.
query productNameandPrice($testid : String = "2") {
products(id:$testid){
name
}
}
More you can read from here
2. Mutations
Mutations allow you to modify server-side data, and it also returns an object based on the operation performed. It can be used to insert, update, or delete data.
How do we know what all actions we can perform? Graphql Voyager is here to help
Select Mutation
Now all the available actions are visible to us.
2.1. Add Mutations
You can add new objects of the main types with the corresponding add mutations. If you do this you have to provide all attributes for the object.
Query adding a category products 5, 6, 7
mutation addcatagory{
addCategory(id: 1, name:"Green Fruits", products:[5,6,7]){
name
products {
name
}
}
}
Let's see if it got updated
query{
categories {
id
name
products{
name
id
}
}
}
2.2. Update Mutations
Changes an object of a type specified by the provided id. You can change all or just certain attributes depending on which attributes you provide. The state of the object after the modification is returned as well.
Query changing name und products of the category with id 1
mutation{
updateCategory(id: 1, name: "Healthy Fruits", products: [7]){
name
products{
name
}
}
}
Let's see if it got updated
2.3. Delete Mutations
Deletes object with provided id. The deleted object gets returned.
Let's delete category 1
mutation {
deleteCategory(id: 1) {
name
}
}
Let's see if it actually got deleted
Yep! it got deleted.
Resources
Learn more here
I still remember the first GraphQL pentest I did, where I manually crafted the queries and thought there might be better way! Yes there is and we will discuss that in our next blog.
Put your brain to use
I challenge you!
- Navigate to the repo graphql-apis and take any publically available GraphQL API
- Check if the Introspection Query is enabled or not?
- If yes, then visualize the GraphQL Schema with your favorite method.
- Start crafting your own Query and Mutations.
- Share this blog with your friends!