Pentesting GraphQL
Common Directories
Basic Operations - Queries
We can fetch field information by sending queries.
Fields
To fetch a field object, send a query like the following.
Arguments
We can get the specific information by padding arguments (e.g. id
) to fields.
Aliases
We can set aliases each field to get multiple results in one request.
Fragments
We can define arbitrary fragment that is be reusable when fetching each field.
query {
firstUser: user (id: "1") {
...userFields
}
secondUser: user (id: "2") {
...userFields
}
fragment userFields on User {
name
age
friends {
name
}
}
}
Operation Names
We can define an operation name to make an operation less ambiguous. By setting a name, it makes it easier to understand at a glance what kind of operation.
Variables
Directives
We can filter by passing a directive in fields.
- include
Only include this field if the argument is true
.
query UserNameAndFriends($userId: ID, $withFriends: Boolean!) {
user(id: $userId) {
name
friends @include(if: $withFriends) {
name
}
}
}
- skip
Skip this field if the argument is true
.
query UserNameAndFriends($userId: ID, $withFriends: Boolean!) {
user(id: $userId) {
name
friends @skip(if: $withFriends) {
name
}
}
}
Basic Operations - Mutations
We can modify fields with the mutation
field.
To modify a field, execute like the following.
mutation CreateCommentForPost($postId: ID!, $comment: Comment!) {
createComment(id: $postId, comment: $comment) {
comment
}
}
Enumeration
# List fields
query { __schema { types { name } } }
query { __schema { types { fields { name } } } }
query { __schema { types { fields { name description } } } }
query { __schema { types { name fields { name } } } }
query { __schema { types { name fields { name args { name description type { name kind ofType { name kind } } } } } } }
# Dump database schema
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 } } } } } } }} query IntrospectionQuery { __schema { queryType { name } mutationType { name } types { ...FullType } directives { name description locations args { ...InputValue } } } }
# Dump specific field
query { getUsers { username, password } }
SQL Injection
We might be able to inject SQL somewhere e.g. arguments. Please refer to SQL Injection Cheat Sheet for more payloads.
NoSQL Injection
We might be able to inject NoSQL somewhere e.g. arguments. Please refer to NoSQL Injection for more payloads.
References
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/GraphQL%20Injection
- https://graphql.org/learn/queries/
- https://escape.tech/blog/pentest101/