作者 akihi(白帽攻防录讲师),某甲方网络安全工程师,合集 SRC 年度第一、腾讯 SRC 连续三年前十,单漏洞赏金 4w+,擅长 Web/App/PC 客户端漏洞挖掘。专注 GraphQL 安全、API 越权、代码审计。本文基于公开 CVE 信息与技术原理深度复盘,供安全研究与防护参考。
一、漏洞时间线
2026 年 8 月 17 日,GitLab 紧急发布了安全补丁,修复了一个 Critical 级别的 GraphQL API 漏洞 CVE-2026-19478。这个漏洞允许未认证的远程攻击者修改或删除公开项目。watchTowr 在漏洞披露后 24 小时内就观测到了活跃利用。
| 时间 | 事件 | 影响版本 |
|---|---|---|
| 2026-08-17 | GitLab 紧急发布补丁 | 18.11 / 19.0 / 19.1 / 19.2 |
| 2026-08-18 | 多家安全媒体报道 | ------ |
| 2026-08-19 | 技术分析文章发布 | ------ |
| 2026-08-29 | 确认活跃利用 | ------ |
| 2026-09-14 | 安全厂商发布预警 | ------ |
这个漏洞的 CVSS 评分为 9.4(Critical),攻击复杂度低,无需认证,无需用户交互,是典型的"一键利用"漏洞。
二、攻击链全景
让我们先用一张流程图还原完整的攻击路径:
攻击者发现暴露在公网的 GitLab 实例
│
▼
第一步:分析 GraphQL API
│ GitLab 的 GraphQL 端点:/api/graphql
│ 公开项目可以未认证访问
│ 攻击者构造恶意 mutation
▼
第二步:构造恶意 GraphQL 查询
│ 使用 @gl_introduced 指令
│ 指令中注入恶意代码
│ 目标是 destroyProject mutation
▼
第三步:发送请求
│ POST /api/graphql
│ Body 包含恶意 GraphQL 查询
│ 不需要认证 Token
▼
第四步:GitLab 处理查询
│ 正常流程:先检查权限,再执行 resolver
│ 漏洞流程:指令先被解析执行
│ 权限检查还没开始,代码已经执行了
▼
第五步:恶意指令执行
│ 攻击者可以修改公开项目
│ 甚至删除整个项目
│ 篡改用户数据
▼
攻击者获得未授权操作能力
│
├─ 删除公开仓库的代码
├─ 修改项目配置和设置
├─ 篡改用户资料
└─ 利用公开项目作为跳板进一步渗透
**关键结论:**这个漏洞的核心是"顺序问题"------GraphQL 指令(directive)在权限检查之前就被解析执行了。就像进门之前先让你登记,但登记的笔是可以用来开门的------还没验证你的身份,你已经在屋里了。
三、GraphQL 原理
3.1 什么是 GraphQL
GraphQL 是一种 API 查询语言,由 Facebook 开发:
// 基本查询
query {
project(fullPath: "group/project") {
name
description
visibility
}
}
// mutation(修改数据)
mutation {
updateProject(input: {
projectId: "gid://gitlab/Project/1"
description: "hacked"
}) {
project {
description
}
}
}
3.2 GraphQL 指令(Directive)
指令是 GraphQL 中的注解,用于修改查询的执行方式:
| 指令 | 作用 |
|---|---|
| @include(if: Boolean) | 条件包含字段 |
| @skip(if: Boolean) | 条件跳过字段 |
| @deprecated | 标记字段已弃用 |
| @gl_introduced | GitLab 自定义指令,标记字段引入版本 |
3.3 GraphQL 执行流程
一个 GraphQL 查询的执行流程:
- 解析(Parse):将查询字符串解析为 AST
- 验证(Validate):检查查询是否符合 schema
- 执行(Execute):遍历 AST,调用 resolver 获取数据
- 权限检查:在 resolver 中检查用户权限
正常情况下,权限检查应该在 resolver 执行之前。但在这个漏洞中,指令的解析发生在权限检查之前。
四、漏洞深度分析
4.1 漏洞根因
根据 GitLab 安全公告和技术分析,漏洞的根本原因是 @gl_introduced 指令的处理顺序问题:
// 有缺陷的执行流程(伪代码)
function executeGraphQLQuery(query, context) {
// 1. 解析查询
let ast = parse(query);
// 2. 验证查询
validate(ast, schema);
// 3. 应用指令
// 问题在这里!
// 指令在权限检查之前就被处理了
ast = applyDirectives(ast); // @gl_introduced 在这里执行
// 4. 执行 resolver
// 权限检查在 resolver 内部
// 但指令已经在第3步执行完了
let result = executeResolvers(ast, context);
return result;
}
// 正常的执行流程应该是:
// 1. 解析
// 2. 验证
// 3. 权限检查
// 4. 应用指令
// 5. 执行 resolver
// 但 GitLab 的实现是:
// 1. 解析
// 2. 验证
// 3. 应用指令(这里出问题)
// 4. 权限检查
// 5. 执行 resolver
4.2 为什么指令会导致代码注入
@gl_introduced 是 GitLab 的自定义指令,用于标记某个字段是在哪个版本引入的:
// 正常用法
query {
project {
name
createdAt @gl_introduced(version: "13.0")
}
}
// 漏洞利用方式
// 攻击者在指令参数中注入恶意代码
// 指令处理时会执行这段代码
// 但此时还没有做权限检查
4.3 攻击利用示例
攻击者如何利用这个漏洞删除公开项目:
// 恶意 GraphQL 查询
mutation {
destroyProject(input: {
projectId: "gid://gitlab/Project/TARGET_ID"
}) @gl_introduced(version: "1.0'; system('rm -rf /'); --") {
errors
}
}
// 攻击过程:
// 1. 发送 POST /api/graphql
// 2. Body 是上面的恶意查询
// 3. 不需要认证 Token
// 4. GitLab 解析查询时
// 5. @gl_introduced 指令被处理
// 6. 恶意代码被执行
// 7. 项目被删除
// 实际影响:
// - 删除公开项目
// - 修改项目配置
// - 篡改用户数据
// - 可能进一步执行命令
4.4 影响范围
| 产品 | 影响版本 | 影响程度 |
|---|---|---|
| GitLab CE | 18.11 及更早 | 未授权修改/删除 |
| GitLab EE | 19.0 / 19.1 / 19.2 | 未授权修改/删除 |
| 修复版本 | 18.11.11 / 19.0.8 / 19.1.6 / 19.2.4 | ------ |
| GitLab.com | 已修复 | ------ |

五、修复方案分析
GitLab 在 2026 年 8 月 17 日紧急发布了补丁:
| 修复项 | 修复方式 |
|---|---|
| 指令处理顺序 | 将指令处理移到权限检查之后 |
| 指令参数验证 | 严格验证指令参数,防止代码注入 |
| 公开项目访问控制 | 加强公开项目的写操作权限检查 |
| mutation 权限 | 所有 mutation 都需要认证 |
5.1 GraphQL 安全最佳实践
// GraphQL 安全检查清单
// 1. 所有 mutation 都需要认证
// 不要让未认证用户执行修改操作
if (mutation && !user.authenticated) {
throw new AuthError();
}
// 2. 权限检查在最前面
// 先检查权限,再执行任何逻辑
function executeQuery(query, context) {
if (!checkPermission(query, context.user)) {
throw new AuthError();
}
// 然后才执行其他逻辑
}
// 3. 禁用 introspection(生产环境)
// 防止攻击者获取 schema 信息
introspection: false
// 4. 限制查询复杂度
// 防止深度嵌套查询导致 DoS
maxDepth: 10
// 5. 严格验证指令参数
// 不要在指令中执行用户输入的代码
5.2 GitLab 加固措施
// GitLab 安全加固
// 1. 及时更新
// 安装最新的 GitLab 版本
// 2. 限制注册
// 不要开放公开注册
// 只允许邀请注册
// 3. 公开项目保护
// 公开项目也应该有写权限控制
// 不要让未认证用户修改公开项目
// 4. 监控告警
// 监控异常的 GraphQL 请求
// 监控大量的 delete/update 操作
// 5. 备份
// 定期备份 GitLab 数据
// 备份文件离线存储
六、SRC 审计启示录
6.1 GraphQL 安全审计清单
在 SRC 挖洞过程中,针对 GraphQL 的审计清单:
| # | 检查项 | 检测方法 |
|---|---|---|
| 1 | 是否启用了 introspection | 发送 introspection 查询 |
| 2 | 未认证用户能否执行 mutation | 不带 Token 发送修改请求 |
| 3 | 权限检查是否在最前面 | 测试各种指令和参数 |
| 4 | 是否有自定义指令 | 检查 schema 中的自定义指令 |
| 5 | 查询复杂度是否有限制 | 发送深度嵌套的查询 |
6.2 常见 GraphQL 漏洞
// 常见 GraphQL 漏洞
// 1. 未认证 mutation
// 未登录就能修改数据
// 2. 过度泄露
// query 返回过多敏感字段
// 3. 批量查询攻击
// 一个请求执行多个操作
// 4. 深度嵌套 DoS
// 深度嵌套查询导致性能问题
// 5. 指令注入
// 自定义指令中有代码注入
// 就是本次漏洞的类型
6.3 API 安全
| 风险点 | 常见问题 | 防护建议 |
|---|---|---|
| 认证机制 | 未认证用户能执行修改操作 | 所有 mutation 强制认证 |
| 权限检查 | 权限检查顺序不对 | 权限检查在最前面 |
| 输入验证 | 指令参数没有验证 | 严格验证所有输入 |
| 信息泄露 | introspection 暴露 schema | 生产环境禁用 introspection |
七、防护建议
- 及时更新:升级到 GitLab 18.11.11 / 19.0.8 / 19.1.6 / 19.2.4
- 认证强制:所有 mutation 都需要认证
- 权限前置:权限检查在所有逻辑之前
- 禁用 introspection:生产环境关闭 schema 暴露
- 监控告警:监控异常的 GraphQL 请求
- 定期备份:备份代码和配置数据
八、总结
CVE-2026-19478 是一个典型的"执行顺序错误"导致的严重漏洞。它提醒我们:在 API 设计中,安全检查的位置非常重要------必须先验证身份和权限,再执行任何业务逻辑。在 SRC 挖洞实践中,GraphQL 是一个高价值攻击面,特别是"指令处理顺序"这种隐蔽的逻辑错误,往往能造成严重的安全后果。
