GraphQL 高危漏洞实战:权限缺失不止密码重置,“全功能越权”才是致命隐患
一、先搞懂:GraphQL 到底是什么?
在聊漏洞之前,先科普下 GraphQL 的核心逻辑,老手可直接跳过——毕竟很多开发者都在用 GraphQL,却未必真正理解它的架构特性,这也是漏洞频发的根源。
GraphQL(Graph Query Language,图查询语言),是由 Facebook 2012 年内部研发、2015 年开源的 API 查询语言与服务器端运行时,核心定位是替代传统 REST API,解决 REST 接口“数据冗余”“多接口多请求”的痛点,目前已被 GitHub、PayPal 等众多平台广泛采用。
和 REST API 相比,GraphQL 有两个最核心的特点,也是它的优势,同时也是安全漏洞的重灾区:
1. 单一端点,所有操作统一入口
REST API 是“多接口多路径”,比如查询用户用 /api/user、修改密码用 /api/user/updatePwd、删除内容用 /api/article/delete,可以通过 URL 路径直接做权限区分;而 GraphQL 所有操作(查、改、删、添、上传)都走同一个端点(通常是 /graphql),后端无法通过 URL 路径判断操作权限,只能依赖 Resolver(解析函数)做校验。
2. 客户端按需请求,灵活性极高
REST API 会固定返回整包数据(比如请求/api/user/1,会返回用户所有字段),容易造成数据冗余;而 GraphQL 允许客户端自主指定需要的字段,比如只查用户名和邮箱,就不会返回密码、手机号等多余信息,极大提升了网络效率,尤其适合移动端场景。
举个简单的 GraphQL 查询示例(查询用户信息):
query {
user(id: "1") {
name # 只请求用户名
email # 只请求邮箱
}
}
响应结果会和查询结构完全一致,不多返回任何冗余数据——这是它的优势,但也埋下了安全隐患:灵活性越高,权限控制的难度就越大,一旦校验缺失,攻击者就可能利用该漏洞获取非授权数据或操作。
补充一句:GraphQL 并非数据库查询语言(如 SQL),而是客户端与服务端之间的数据交互协议。其权限控制的核心在于 Resolver 的实现。
二、步入正题:GraphQL 权限缺失,隐患不容忽视
网上关于 GraphQL 安全的文章,大多集中在“信息泄露”“内省查询”“深度嵌套 DoS”这三类,却极少有人关注 Mutation(增删改操作)的权限控制缺失——而这正是我近期在真实渗透测试中发现的致命漏洞,也是本文的核心:
GraphQL 权限控制若存在缺失,各类操作(查、改、删、添、上传、查看个人信息)都可能被越权执行,任意用户密码重置就是其中典型且高危的场景。
先从最直观、最高危的「任意用户密码重置」说起——这是我第一个发现的漏洞,也是最容易被忽视、网上报道极少的场景。
1. 实战漏洞:任意用户密码重置(无限制、无验证)
本次测试的系统的 GraphQL 接口为 POST /mmm-dgraph-backend-http-server/graphql,核心漏洞出在 updateUser 这个 Mutation 操作上(用于修改用户信息)。
1.1 漏洞触发场景
系统新用户注册时,默认密码为 6 个 6,登录后会强制修改新密码。此时抓包发现,修改密码的请求正是调用 updateUser 接口,传入 loginUserName(目标账号)、newPassword(新密码)两个核心参数,无任何其他校验。
关键发现:无论目标用户是否改过密码、改了多少次、密码多复杂,只要传入其账号,就能直接覆盖重置密码——学生、老师、管理员等各类角色均可能受影响,无需原密码、无需短信/邮箱验证码,普通用户即可操作。
1.2 越权重置 Payload 示例
mutation UpdateUser($targetAccount: String!, $newPwd: String!, $updateTime: Date!) {
updateUser(
filter: { account: $targetAccount },
set: {
password: $newPwd,
updatedAt: $updateTime
}
) {
user {
account
updatedAt
}
}
}
请求变量(自定义目标账号和新密码):
{
"targetAccount": "targetUser", # 目标账号(示例,非真实账号)
"newPwd": "examplePwd123.", # 自定义新密码(示例)
"updateTime": "2026-04-12T10:00:00Z"
}
1.3 漏洞本质(核心逻辑问题)
这个漏洞的本质,不是某个接口的“小bug”,而是 GraphQL 权限设计的根本性逻辑错误,后端 Resolver 代码的核心问题的是:只校验“用户是否登录”,不校验“当前用户是否有权限修改该账号”,也不校验“数据归属权”。
用伪代码还原漏洞现场(错误逻辑):
# 漏洞代码(后端 Resolver 逻辑)
def resolver_updateUser(args, context):
# 只校验是否登录,不校验操作权限
if not context.current_user:
return PermissionDenied("请先登录")
# 直接根据传入的 loginUserName 定位用户,覆盖密码
loginUserName = args['filter']['login_user_name']
newPassword = args['set']['password']
db.user.update_one(
{"login_user_name": loginUserName},
{"$set": {"password": hash(newPassword)}}
)
return {"success": True}
一句话总结:只认账号,不认人;只认登录,不认权限;无旧密码、无二次验证,直接覆盖——这属于典型的“失效的访问控制”(OWASP Top 1 2021),也是 GraphQL Mutation 操作中常见的安全疏漏。
2. 延伸风险:权限缺失可能导致全功能越权
发现任意用户密码重置漏洞后,进一步测试发现:若权限控制存在缺失,GraphQL 的各类操作都可能被越权执行——这是由 GraphQL 单一端点的架构特性决定的,也是目前相关安全报道中较少提及的风险点。
结合本次测试场景,以及 GraphQL 通用安全特点,若权限控制不到位,以下操作均可能出现越权情况(普通用户操作其他用户或管理员数据):
2.1 越权查看:获取任意用户/管理员敏感信息
通过 queryUser、queryAllStudents 等 Query 操作,普通用户可直接查询其他用户的手机号、身份证号、成绩、考勤等敏感信息,甚至可通过 queryAllAdmins 获取所有管理员账号信息——这正是我之前发现的“全校身份信息泄露”漏洞,与密码重置漏洞形成“组合拳”:先批量获取账号,再批量重置密码。
越权查看 Payload 示例:
query {
user(login_user_name: "admin_001") { # 管理员账号
name
identity_card # 身份证号(明文)
mobile_number # 手机号(明文)
role # 角色信息(管理员)
}
}
2.2 越权修改:篡改任意用户信息
除了重置密码,通过 updateUser 接口,还可能修改任意用户的手机号、邮箱、头像、角色等信息——例如将普通用户角色改为“admin”获取管理员权限,或修改他人手机号接收验证码,进而接管账号。
2.3 越权删除:删除任意用户/内容
若系统存在 deleteUser(删除用户)、deleteArticle(删除文章)等 Mutation 操作,普通用户可能直接删除其他用户的账号、发布的内容,甚至删除管理员账号,影响系统正常运行。
2.4 越权上传:上传恶意文件到任意目录
若系统存在 uploadFile 接口,普通用户可能上传恶意文件(如webshell)到管理员目录、其他用户空间,进而获取服务器权限,对系统造成进一步渗透影响。
2.5 越权发布:冒充他人发布内容
通过 createArticle、createNotice 等接口,普通用户可能传入他人的账号 ID,冒充老师、管理员发布通知、公告,甚至发布不良信息,造成负面影响。
这里有一个核心结论,也是本文最有价值的观点(建议单独加粗放在文章中):
GraphQL 越权并非单点漏洞,可能引发系统性安全风险。若权限控制缺失,查看、修改、删除、上传、发布、重置密码等操作,都可能被越权执行。
3. GraphQL 易出现全功能越权的原因
核心原因在于:开发者对 GraphQL 权限设计的理解存在疏漏,容易混淆“登录校验”和“权限校验”,具体主要有3点:
1. 误区一:“只要用户登录了,就可以调用所有接口”——忽略了“数据归属权”,比如普通用户只能操作自己的账号,不能操作他人的;
2. 误区二:“GraphQL 只需要做入口校验”——忽略了 GraphQL 单一端点的特性,无法通过 URL 控制权限,必须在每个 Resolver、每个字段上做细粒度校验;
3. 误区三:“Mutation 操作和 Query 操作权限一致”——忽略了 Mutation 是“写操作”(增删改),风险远高于 Query(读操作),需要更严格的权限控制和二次验证。
对比 REST API 和 GraphQL 的权限控制差异,更能理解这个问题:
|
对比维度 |
REST API |
GraphQL |
|---|---|---|
|
权限控制方式 |
可通过 URL 路径、HTTP 方法做粗粒度控制 |
只能通过 Resolver 做细粒度控制(字段级、操作级) |
|
操作入口 |
多接口、多路径 |
单一接口(/graphql) |
|
开发者易犯错误 |
路径权限配置遗漏 |
只做登录校验,不做数据归属权校验 |
三、漏洞危害与实战利用链
2. 完整利用链(从信息泄露到系统接管)
结合本次测试发现的两个核心漏洞,完整利用链如下(可直接写进漏洞报告):
Step 1:通过 GraphQL 内省查询(未关闭),获取系统所有 Query、Mutation 接口,确认 queryAllStudents(批量查询用户)和 updateUser(修改用户)接口的存在;
Step 2:调用 queryAllStudents 接口,传入 first: 100000、offset: 0,批量获取全校所有用户的 login_user_name(学号/手机号);
Step 3:调用 updateUser 接口,批量重置所有用户(含管理员)的密码,自定义新密码;
Step 4:用重置后的密码,登录任意目标用户账号,接管账号后,可能进一步越权修改、删除数据或上传文件,对系统安全造成严重影响。
四、漏洞修复建议方案
针对 GraphQL 权限缺失导致的全功能越权,修复的核心是“做细粒度的权限控制”,从紧急修复到长期加固,分两步落地:
1. 修复建议
1. 强数据归属权校验(核心修复):所有 Query、Mutation 接口,必须校验“当前登录用户 ID == 目标用户 ID”,普通用户只能操作自己的账号,管理员只能操作授权范围内的账号;
正确 Resolver 逻辑(伪代码):
def resolver_updateUser(args, context):
currentUser = context.current_user # 当前登录用户
# 1. 校验是否登录
if not currentUser:
return PermissionDenied("请先登录")
# 2. 定位目标用户
targetUser = db.user.find_one({"login_user_name": args['filter']['login_user_name']})
# 3. 核心校验:只能修改自己的账号(管理员需额外判断角色)
if currentUser.id != targetUser.id and currentUser.role != "admin":
return PermissionDenied("无权限修改该用户信息")
# 4. 校验原密码(新增)
if not verify_password(args['oldPassword'], targetUser.password):
return PermissionDenied("原密码错误")
# 5. 执行密码更新
db.user.update_one(...)
return {"success": True}
2. 增加二次验证:修改密码、删除账号、修改角色等敏感操作,必须增加原密码校验、短信/邮箱验证码二次验证,禁止无验证直接操作;
3. 限制批量操作:单次请求只能操作一个用户/一条数据,禁止批量修改、批量删除;
4. 关闭生产环境内省查询:禁用 __schema、__type 等内省查询,避免接口结构泄露,不给攻击者“全图透视”的机会;
5. 敏感字段脱敏:身份证号、手机号等敏感信息,仅本人和管理员可查,且必须脱敏(如 1****************9)。
2. 长期加固
1. 实现字段级+操作级双重权限控制:每个字段、每个 Query/Mutation 操作,都要单独做权限校验,不能一刀切;
2. 区分 Query 和 Mutation 权限:Mutation 操作(增删改)的权限控制要比 Query 操作(读)更严格;
3. 做查询复杂度、深度限制:防止深度嵌套查询导致 DoS 攻击,同时限制单次查询的字段数、数据量;
4. 全量日志审计:记录所有 GraphQL 操作(请求 IP、用户、操作内容、时间),便于漏洞溯源和攻击排查;
5. 定期安全测试:针对 GraphQL 接口,重点测试越权、注入、批量操作等漏洞,避免类似问题重复出现;
6. 规范 Resolver 开发:将权限控制逻辑抽离为公共工具函数,避免重复开发导致的校验遗漏,同时将权限控制移至业务逻辑层,而非仅在 Resolver 中简单校验。
五、漏洞挖掘思路
结合本次实战经验,给大家分享一套 GraphQL 越权漏洞的挖掘思路,适用于所有使用 GraphQL 的系统,新手也能快速上手:
1. 第一步:判断系统是否使用 GraphQL——查看请求地址,若存在 /graphql 端点,且请求方法为 POST,大概率是 GraphQL 接口;
2. 第二步:尝试内省查询,获取所有接口结构——通过 __schema 查询,获取所有 Query、Mutation 接口,以及参数、返回字段;
3. 第三步:测试 Query 接口越权——用普通用户登录,尝试查询其他用户、管理员的信息,看是否能成功返回;
4. 第四步:测试 Mutation 接口越权——重点测试 updateXXX、deleteXXX、createXXX 等操作,尝试操作其他用户的数据;
5. 第五步:测试批量操作——尝试在一个请求中,批量查询、批量修改多个用户的数据,看是否能绕过限制;
6. 第六步:测试敏感操作——重点测试密码重置、角色修改、文件上传等操作,看是否需要二次验证、是否有权限校验。
六、总结与思考
GraphQL 的灵活性是其显著优势,但也使其成为安全漏洞的高发领域。本文所剖析的「GraphQL 权限缺失可能导致全功能越权」问题,之所以相关报道较少,核心原因是:多数开发者、安全从业者,更多关注 GraphQL 的“查询侧”安全,而忽视了“修改侧”(Mutation)的权限控制——这恰恰是容易引发高危风险的环节。
最后,用一句金句总结本文核心,也希望能提醒所有开发者:
GraphQL 安全离不开登录校验、操作权限校验、数据归属权校验,三者缺一不可。任何一项缺失,都可能导致系统出现越权漏洞,给攻击者可乘之机。
希望本文能填补网上 GraphQL 安全领域的相关空白,让更多开发者重视 GraphQL 权限设计,也让更多安全从业者掌握这类漏洞的挖掘思路,共同提升系统安全防护水平。
声明(合规必加)
本文仅用于网络安全技术交流、漏洞防御科普,所有案例均为授权测试或已修复场景。严禁未经授权对任何系统进行渗透测试,违者自行承担法律责任。
创作不易,收藏+关注,后续持续更新实战漏洞分析、渗透测试技巧!
更多推荐




所有评论(0)