Cross-user read and modification of chat-group agents via unscoped junction-table queries (IDOR)

该问题在 LobeChat 自部署环境(Self hosting Docker)下触发,涉及 tRPC 路由中的群组代理人关联表( chat_groups_agents junction table)操作。攻击者通过构造 HTTP POST 请求直接传入目标群组 ID(groupId),无需所有权验

Cross-user read and modification of chat-group agents via unscoped junction-table queries (IDOR)

Cross-user read and modification of chat-group agents via unscoped junction-table queries (IDOR)

快速结论:该安全漏洞出现在 LobeChat 自部署(Docker)场景的 2.2.1 版本中,表现为已认证用户可越权读取、修改或删除其他用户的聊天群组代理人(agent)关联记录。优先排查 removeAgentsFromGroup 方法是否仍缺少 userId 作用域限制。

问题场景

该问题在 LobeChat 自部署环境(Self hosting Docker)下触发,涉及 tRPC 路由中的群组代理人关联表(chat_groups_agents junction table)操作。攻击者通过构造 HTTP POST 请求直接传入目标群组 ID(groupId),无需所有权验证即可执行越权操作。核心影响模块为 packages/database/src/models/chatGroup.tspackages/database/src/repositories/agentGroup/index.ts

报错原文

// 该漏洞无直接报错,攻击者通过正常 API 调用即可成功越权操作
// 以下为 PoC 中使用的 tRPC 请求示例:
POST /trpc/lambda/agentGroup.getGroupAgents
{ "groupId": "" }

POST /trpc/lambda/agentGroup.updateAgentInGroup
{ "groupId": "", "agentId": "", "updates": { "role": "...", "order": 0 } }

POST /trpc/lambda/agentGroup.removeAgentsFromGroup
{ "groupId": "", "agentIds": [""] }

原因分析

这是典型的 IDOR(Insecure Direct Object Reference) 安全漏洞。根本原因在于 chat_groups_agents 联接表(junction table)中的三个操作缺少 userId 作用域检查:

  • getGroupAgentschatGroup.ts:255-260)仅按 chatGroupId 查询,无 userId 谓词
  • updateAgentInGroupchatGroup.ts:219-231)更新时只过滤 chatGroupIdagentId,缺少 userId
  • removeAgentsFromGroupagentGroup/index.ts:291-295)删除操作仅按 chatGroupIdagentId 过滤,无 userId 谓词

上层路由的 agentGroupProcedureagentGroup.ts:42)虽然注入了 ctx.userId,但并未对 groupId 进行所有权验证。相比之下,getGroupDetail/findByIdaddAgentsToGroup/findGroupWithAgents 使用了 findById 先进行主人作用域查询,因此是安全的。

环境排查

  • LobeChat 版本:2.2.1(Issue 报告版本)
  • 部署平台:Self hosting Docker
  • 数据库:使用 chat_groups_agents 表(包含 user_id 列和索引)
  • 受影响的代码文件packages/database/src/models/chatGroup.tspackages/database/src/repositories/agentGroup/index.ts

解决步骤

  1. 确认已修复的内容getGroupAgentsupdateAgentInGroup 已在后续提交中加入了 this.agentsOwnership() 作用域检查。可优先验证这两个方法是否已经包含 userId 谓词。
  2. 修复 removeAgentsFromGroup(可优先尝试):在 packages/database/src/repositories/agentGroup/index.ts 的删除查询中添加 this.agentsOwnership() 或通过 owner-scoped 的 findById 门控。当前问题代码(约 542-546 行)为:
    await this.db
      .delete(chatGroupsAgents)
      .where(
        and(eq(chatGroupsAgents.chatGroupId, groupId), inArray(chatGroupsAgents.agentId, agentIds)),
      );

    修复后的代码应增加 userId 谓词:and(eq(chatGroupsAgents.chatGroupId, groupId), eq(chatGroupsAgents.userId, this.userId), inArray(...))

  3. 参考已有修复模式:可以参照 #16346 和 #16352 PR 中应用的 agentsOwnership() 模式,该模式已在其他两个方法中验证有效。
  4. 更新代码并重新构建:修改完成后,需要重新构建 Docker 镜像并部署。

验证方法

验证问题已修复:使用两个不同的浏览器/用户会话,尝试通过以下请求读取或修改另一个用户群组的代理人记录:

  • POST /trpc/lambda/agentGroup.getGroupAgents 应返回空结果或权限错误
  • POST /trpc/lambda/agentGroup.updateAgentInGroup 应返回权限错误
  • POST /trpc/lambda/agentGroup.removeAgentsFromGroup 应返回权限错误或删除记录数为 0

如果所有越权操作都被拒绝,则修复成功。同时确认正常的群组所有者仍可正常操作自己的群组。

参考来源

lobehub/lobe-chat #16537

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

celebrityanime
celebrityanime
文章: 12080

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注