Skip to content

多用户:聊天发送路径缺 thread 归属校验(潜在 IDOR,分享/导出会话前必修) #130

Description

@mirror29

背景

PR #129(多用户登录)修了 chat 会话(listChatMessages)和改标题(setChatThreadTitle)两处越权,靠 apps/dashboard/src/lib/mastra.tsownsThread(取回 thread 校验 resourceId === 登录用户)。但聊天发送/续接主路径没有同源校验。

问题

  • /api/copilotkit(apps/dashboard/src/app/api/copilotkit/route.ts)把 resourceId 强制设为 getSessionSubject()(可信),但 threadId 来自前端 <CopilotKit threadId>(客户端 crypto.randomUUID() 生成、存 localStorage,客户端可控)。
  • mastra 侧 packages/orchestration/src/mastra/memory.tsassertScopedRequest() 只校验 resourceId/threadId 非空,不比对已存在 thread 的 resourceId 是否等于调用方;且该函数目前只在 scheduler/runner.ts 调,聊天路径没调
  • identityMiddleware(mastra/index.ts)注释明确写它与 Memory 的 resourceId 机制"完全解耦",只管 AUTH_SUB_KEY 给 askCache 用,不 reconcile 请求体里的 resourceId/threadId

影响(潜在,非当前可利用)

登录用户 B 若拿到用户 A 的 threadId,向 /api/copilotkit 发消息传 threadId=A的thread → mastra 侧无环节校验该 thread 归属 → B 的消息写进 A 的会话、B 本轮还可能读到 A 的历史上下文;A 下次打开该会话看到 B 注入的消息。

当前不可利用:threadId 是不可枚举的 UUIDv4,从不展示给其他用户、不进 URL、无"分享会话"功能 → 今天没有获取他人 threadId 的向量。属防御纵深缺口。

触发条件(务必在此之前修)

加任何会让 threadId 泄露的功能之前必须先修:分享会话 / 导出会话 / 把 threadId 放进 URL / 客服可见的会话链接 等。

建议修法

服务端(正确层次):在 mastra agent.stream/generate 前(或 identityMiddleware / 一个 input processor 里)对已存在的 threadId 加一次 resourceId 归属校验,复用 PR #129 ownsThread 思路——thread 存在且 resourceId !== 已认证 sub → 拒绝;不存在(新会话)或本人 → 放行。需先读 @mastra/memory 内部确认拦截点(PR #129 review 因沙箱无 node_modules 未能二次交叉确认,置信度高但非 100%)。

备选:dashboard BFF 在 /api/copilotkit 解析 threadId 做同样 3 态校验(新建/本人/他人),但要解析并重建 CopilotKit 请求体流,较 fragile。

来源

PR #129 第 5 轮 auto-review。同根问题的兄弟修复已在该 PR 落地(读/改标题两处)。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions