GraphQL 发现与 Introspection 信息泄露
GraphQL 把“接口”折叠成一个端点:所有查询都发往同一 URL,__schema 这类内置字段又能让客户端 枚举整个 schema。测试的第一步是找到端点,第二步是确认 introspection 是否开放。
端点指纹
GraphQL 端点对非 POST 请求有特征回应。最常见的探测是直接 GET:
curl -sS http://target/graphql
# GET query missing.
返回 GET query missing. 之类的提示,基本可以确认这是 GraphQL 端点。
其他辅助特征:
| 特征 | 说明 |
|---|---|
| GET 返回 query missing 提示 | 端点存在但请求方式不对 |
| CORS 全开 + Express 弱 ETag | Apollo Server(基于 Express)的框架特征组合 |
| 错误堆栈泄露绝对路径 | 服务端调试信息未关闭,泄露部署结构 |
# 观察响应头特征
curl -sSI -H 'Origin: http://evil.example' http://target/graphql
常见位置
端点不一定在根路径,先枚举常见路径再确认指纹:
/graphql
/graphiql
/api/graphql
/v1/graphql
路径发现流程可参考 ffuf 快速模糊枚举。
Introspection 还原 schema
GraphQL 内置自省(introspection)能力,标准查询可以直接枚举类型与字段:
query {
__schema {
types {
name
fields {
name
}
}
}
}
从返回结果能还原出完整的 schema:有哪些查询入口、哪些对象类型、每个类型暴露哪些字段。schema 是后续所有测试的地图——没有权限控制时,schema 里写出来的字段往往就是可查的。
直接查询敏感字段
还原 schema 后,观察是否存在把敏感数据直接暴露为普通字段的类型(典型如用户类型同时带 username 与 password):
query {
users {
username
password
}
}
若查询成功返回明文口令哈希甚至明文,即为一次完整拖库。这通常出现在 字段级鉴权缺失 的实现里:接口只做了“能不能进 GraphQL”的判断,没做“哪些字段能不能读”的判断。
Apollo Server 常见暴露
Apollo Server 是最常见的 GraphQL 实现,测试中反复出现同一组暴露面:
| 暴露面 | 风险 |
|---|---|
| 生产环境未关 introspection | 完整 schema 泄露 |
| CORS 全开 | 任意来源可携带跨域请求读取数据 |
| 详细错误堆栈 | 泄露绝对路径与内部结构 |
| 默认无字段级鉴权 | 敏感字段随 schema 一起可查 |
复核要点
| 检查点 | 说明 |
|---|---|
| 端点确认 | GET 特征提示 + 常见路径枚举双确认 |
| introspection 状态 | __schema 查询是否返回完整类型 |
| 敏感字段 | 逐个检查用户、凭据、内部状态类类型 |
| 错误信息 | 故意构造非法查询观察堆栈详细程度 |
| 认证边界 | 无 token 与带 token 的查询结果差分 |
防御建议
- 生产环境禁用 introspection,schema 只在开发/文档环境提供。
- 实施字段级鉴权:敏感字段(口令、内部 ID)在 resolver 层显式校验,而不是只在端点入口判断。
- 限制查询深度与复杂度,防止嵌套查询打瘫后端。
- 收紧 CORS,不使用全开源放。
- 关闭详细错误堆栈,生产环境只返回通用错误信息。