Skip to content

[Desktop/Chat][P0][Security] 闭合聊天实时事件流的服务端认证合同 #78

Description

@jiayuqi7813

范围:桌面聊天的实时消息与侧栏事件传输,以及它依赖的最小服务认证合同。本轮只登记问题,不修改桌面、共享 Web 或后端代码。证据来自当前代码的只读调用链审查、Python AST 检查和公开固定提交 ca65724 的文件对照;没有访问真实事件、聊天、账号或凭据。

确认的缺口:通用认证中间件将事件流认证委托给各处理器。现有运行事件流和终端 WebSocket 在访问数据或接受连接前验证身份,聊天收件箱及聊天消息事件流未执行相同检查。聊天 router 的创建与挂载没有额外认证依赖;消息快照查询使用默认本地 operator 身份,收件箱处理器直接访问聊天摘要。这是服务端身份合同缺口,客户端先登录或先申请票据不能替代该检查。

可达条件与影响:在启用登录保护、聊天数据存在、聊天服务入口可被未认证调用方到达且没有额外网关认证的部署中,当前代码允许事件处理器触及聊天元信息、消息页与后续事件。默认 UI API 转发器没有补充认证;安装路径支持 UI 对外提供服务而 API 保持回环,不能仅凭 API 回环绑定推断此边界受保护。这些是源码与路由挂载共同支持的条件性风险;本次没有执行未认证读取链、读取真实记录、验证安装包运行行为或证明已经发生外泄。单个快照包含当前消息页,不能把它夸称为一次响应必然含全部历史正文。外部独立认证网关及实际网络可达性未核验。

严重性:按 P0 安全修复优先级追踪。启用密码的受支持部署仍未在该传输入口建立身份边界,响应来源包含敏感聊天内容;默认 UI 转发与公开 UI/回环 API 部署路径没有补充入口认证。该评级依据完整源码链中确定的认证缺口及其保密性影响,不依赖已经发生真实外泄。运行性请求、安装包和实际外泄仍未测试;如特定部署另有独立认证网关,其保护范围必须单独验证。本条不宣称命令执行、原生 bridge 越权或修改聊天数据。

上层防线复核:仓库内的登录界面是客户端组件,不是 API 身份校验;平台就绪门只在恢复未完成时拒绝服务;CORS 限制浏览器跨源响应访问,不验证请求者身份,也不替代同源 UI 转发后的服务端认证。查询路由分发保留原 operator 身份,没有新增对 HTTP 调用方的验证。受支持的安装和 Compose 配置没有提供默认独立认证网关。这里仅审查仓库定义的防线,不推断用户实际网络、反向代理或防火墙配置。

证据与对照:

  • apps/web/server.py:338-342:通用认证门委托事件流处理器验证身份。
  • apps/web/conversation_api.py:319-384,743-836,1564-1649:默认 operator 查询身份、无额外 router 认证依赖,以及两个聊天事件处理器的完整读取/响应路径。
  • apps/web/routes/events.py:61-67 与 apps/web/conversation_api.py:1395-1412:现有运行事件与终端连接在访问数据前检查身份,作为正常防护对照。
  • muteki/conversation/inbox.py:94-132,277-283 与 muteki/conversation/manager.py:2174-2193:摘要/线程对象和消息页属于实际响应来源。
  • apps/web/ui/app/api/[...path]/route.ts:44-75:默认 UI 转发请求与响应,不新增登录身份检查;apps/web/ui/middleware.ts:7-12 仅处理终端升级转发。
  • run.sh:114-123、scripts/install-ubuntu.sh:38-42,344:公开 UI 的密码要求与 UI/API 分开绑定条件;这不是本次实际部署记录。
  • apps/web/ui/components/LoginGate.tsx:1,44-80、apps/web/server.py:282-287,364-377、apps/web/platform_stack.py:206-213:客户端登录、CORS、就绪门和查询分发没有补充事件传输的身份检查。

固定提交对照:上述认证、事件处理器、UI 转发器、登录组件、消息来源与启动/安装文件均与公开 ca65724 字节一致。platform_stack.py 本地整体有其他改动,但聊天 router 挂载调用及查询分发的方法源码哈希与公开提交一致;挂载本地位于 564 行,公开提交位于 561 行。完整静态证据、源码哈希和无网络的合成路径字面匹配结果留在本地忽略目录。本条不附可复制的未认证请求、读取脚本或真实截图。

建议处理:将服务身份验证集中为所有聊天事件传输共有的必要入口,在访问摘要/消息、创建订阅或计数前完成验证,并沿用已有运行事件/终端的票据与身份合同。桌面 stream adapter 保留明确的未登录、权限拒绝、连接失败与重连状态,继续维持线程/连接 generation 和事件去重保护。仅修改 renderer 的登录 gate 无法修复服务入口;若桌面融合要承诺聊天身份隔离,最小服务端认证合同是必要依赖,不能通过界面状态或绑定地址掩盖。这里不要求扩大到 Web 布局或产品功能改造。

防御性验收:

  1. 使用空白隔离 profile 和合成数据,所有未完成身份验证的聊天事件传输在接触数据源及建立订阅前被拒绝,已存在的受保护普通查询行为保持一致。
  2. 合法身份或一次性票据按现有合同正常连接;失效、重复使用或错误身份有明确拒绝;认证关闭模式按既有受支持的本机单操作员约定处理。
  3. 经桌面连接地址和默认 UI 转发路径,认证结果一致;回环 API 绑定不代替认证。独立网关如提供额外保护,作为单独部署条件记录。
  4. 重新登录、重连与切换聊天时仍丢弃旧线程/旧连接回调,事件去重和分页历史保持正确。
  5. 验收材料只使用合成聊天和明确拒绝/允许结果,不访问或公开真实用户数据。

去重:关联 桌面聊天融合 Epic #31。#66 是不可信预览与可信 renderer 的同源隔离问题;本条是实时传输入口的服务身份验证缺口,根因独立。#70 处理客户端认证探测错误,#69 和 #72 处理登录失效/旧响应引发的本地状态损失,均不替代服务端入口检查。两个聊天事件处理器归为一条共同认证合同 Issue。

公开固定代码:server.py、conversation_api.py、运行事件对照、公开 router 挂载、默认 UI 转发器、消息来源、启动配置。

补充原处理器隔离复验:使用原 create_conversation_router 创建路由,只给查询层一个内存中的合成消息与合成 operator;不带授权头或一次性票据的合成 Request 调用原聊天线程事件处理器,返回 HTTP 200,首个 snapshot 帧包含该合成消息。完整输入、原函数调用记录和首帧保存在本地 ignored sse-original-handler-synthetic-result.json。此复验没有执行真实认证中间件,也不是从网络或用户聊天进行匿名读取;中间件对该事件路径的委托与缺少上层身份依赖仍依据前述源码审查。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions