Cross-user RAG document disclosure via unscoped semantic search (IDOR)

该问题在自托管 Docker 部署的 LobeChat v2.2.1 上被发现,涉及知识库(Knowledge Base/RAG)功能。当任意两个已认证用户在同一服务器实例上使用时,攻击者可以通过调用 chunk.semanticSearch 或 chunk.semanticSearchForCha

Cross-user RAG document disclosure via unscoped semantic search (IDOR)

Cross-user RAG document disclosure via unscoped semantic search (IDOR)

快速结论:这个 IDOR(不安全的直接对象引用)漏洞发生在 LobeChat 的知识库(Knowledge Base)语义搜索功能中,semanticSearchsemanticSearchForChat 的数据库查询未使用 userId 进行作用域限定,导致任何已认证用户都能检索其他用户的文档片段。优先排查 semanticSearchknowledgeBaseFiles 查询中是否添加了 userId 谓词。

问题场景

该问题在自托管 Docker 部署的 LobeChat v2.2.1 上被发现,涉及知识库(Knowledge Base/RAG)功能。当任意两个已认证用户在同一服务器实例上使用时,攻击者可以通过调用 chunk.semanticSearchchunk.semanticSearchForChat tRPC 过程,实现跨用户文档数据泄露。

报错原文

# 核心漏洞代码(未修复前)
# packages/database/src/models/chunk.ts 中的 semanticSearch 方法
.where(fileIds ? inArray(fileChunks.fileId, fileIds) : undefined)   # 缺失 userId 谓词,当 fileIds 为空时 WHERE 子句为空

# packages/database/src/models/chunk.ts 中的 semanticSearchForChat 方法
.where(inArray(fileChunks.fileId, fileIds))   # 同样缺失 userId 谓词

# apps/server/src/services/knowledgeBase/index.ts 中的 knowledgeBaseFiles 查询
const knowledgeFiles = await this.serverDB.query.knowledgeBaseFiles.findMany({
  where: inArray(knowledgeBaseFiles.knowledgeBaseId, knowledgeIds),   # 无 userId 检查
});

原因分析

根本原因:LobeChat 的语义搜索在多个层级未正确限定用户作用域:

  • Chunk 模型层(chunk.ts):semanticSearchsemanticSearchForChat 方法在查询 chunksembeddingsfileChunks 表时,缺失了 eq(chunks.userId, this.userId) 条件。与已修复的同级方法 findByFileId 不同(该正确添加了 eq(chunks.userId, this.userId)),这两个方法直接将文件 ID 列表传入查询,未做用户所有权检查。
  • 认证绕过:fileIds 参数为空时,WHERE 子句变为空,搜索会返回整个实例中所有用户的文档片段(top-30)。攻击者甚至不需要知道目标用户的任何 ID。
  • 上游知识库解析层(knowledgeBase/index.ts):knowledgeBaseFiles 的解析仅通过 knowledgeBaseId 过滤,未校验 userId。即使下游 chunk 查询已修复,攻击者仍可通过已知的目标用户 knowledgeBaseId 获取其文件 ID,从而绕过部分限制。

环境排查

  • LobeChat 部署版本:v2.2.1 及更早版本(受影响的版本)
  • 部署方式:Self-hosting Docker
  • 操作系统:其他 Linux(该问题与操作系统无关)
  • 相关文件:packages/database/src/models/chunk.tsapps/server/src/services/knowledgeBase/index.ts

解决步骤

  1. 修复 chunk 模型层:packages/database/src/models/chunk.tssemanticSearchsemanticSearchForChat 方法中,添加 this.ownership()(即 buildWorkspaceWhere)到 WHERE 子句,确保查询受当前用户 ID 限定。
  2. 修复知识库解析层:apps/server/src/services/knowledgeBase/index.tsknowledgeBaseFiles 查询中,添加 eq(knowledgeBaseFiles.userId, this.userId) 条件,确保文件 ID 解析时验证文件所有者的用户 ID。
  3. 验证与测试:在修复后,构造两个不同用户的身份,尝试通过语义搜索访问对方文档,确认搜索结果仅包含当前用户自己的文档内容。

验证方法

部署修复后的代码,使用两个不同账户同时登录同一服务器。尝试以下操作:

  • 不提供 fileIds 参数调用语义搜索 API,验证结果中是否只包含当前用户的文档片段。
  • 使用已知的其他用户的 knowledgeBaseId(如果可能)进行搜索,验证是否返回“无权限”或空结果。
  • 检查数据库查询日志,确认 WHERE 子句中存在 userId 过滤条件。

参考来源

lobehub/lobehub #16535

关联 PR:PR #14673(部分修复了 chunk 模型的查询作用域)

安全公告:GHSA-r929-w8h7-f9g6(原始报告)

GamsGo AI

AI 工具推荐

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

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

了解 GamsGo AI

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

celebrityanime
celebrityanime
文章: 10925

发表回复

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