跳到主要内容

← 返回开发日志

连接库、凭据与本地优先的安全边界

已完成 Security
  • #安全
  • #本地优先

本地优先意味着没有账号

ConchTerm 是一个本地桌面客户端,核心能力不需要账号、不依赖云服务、不向我们的后台上报数据。你连的是你自己的服务器,凭据存在你自己的机器上,由 macOS 提供的加密机制保护。

这不是一句口号,而是由具体的进程边界和 IPC 规则支撑的。

进程边界:渲染层碰不到秘密

ConchTerm 的架构是单向依赖的:

renderer -> preload typed facade -> IPC -> main application core

在这个模型里,渲染层(React UI)只能通过 preload 暴露的强类型 window.api 与主进程通信。密码、passphrase、私钥内容、原始 host key 都不会跨 IPC,因此它们根本没有机会进入渲染层的状态。

具体来说:

  • contextIsolationnodeIntegration: falsesandbox: true
  • 渲染层不能直接访问 Node、文件系统或原始 IPC。
  • preload 只暴露固定的、强类型的 API,不暴露 raw ipcRenderer

凭据边界:Main-only

凭据的处理遵守严格的 Main-only 原则:

  • 密码和 passphrase 存在主进程内存中,运行期凭据库不可序列化。
  • 可选的持久化凭据通过 macOS safeStorage 加密后写入本地文件。
  • 连接配置文件里只存非秘密的 profile 信息(host、端口、用户名、认证方式等),不存明文密码。
  • 导入/导出连接库时,会生成一个脱敏的 bundle,不含凭据、私钥引用和绝对路径。

这意味着:即使你导出连接库分享给别人,里面也不会带出你的密码或私钥路径。

IPC 边界:每次调用都校验

每个 IPC 入口都要通过一条统一管线:

  • 运行时 Zod schema 校验输入。
  • 校验调用者的 sender frame。
  • 校验资源所有权(owner-scoped)。
  • 事件只发送给所属的窗口。

这样即使某个窗口被恶意内容影响,它也无法访问其他窗口的会话、凭据或传输任务。关闭一个标签时,对应的连接、任务、路径授权和运行期凭据会被统一清理。

本地路径边界:授权作用域

文件工作台需要访问本地文件,但本地绝对路径不会跨 IPC:

  • 通过文件选择器或拖拽产生的访问被登记为一条 grant,绑定到 owner、带 TTL、可以被 retain 或撤销。
  • 渲染层看到的是 grantId + relativePath,不是本地绝对路径。
  • 所有本地文件操作都在 grant 作用域内解析路径,越界访问会被拒绝。
  • 关闭会话时撤销该 owner 的全部 grant。

诊断边界:可排查但不泄密

诊断导出(diagnostics export)是排查问题时最有价值、也最敏感的功能。ConchTerm 的设计是:导出经过二次脱敏,且默认不包含会话级秘密。

  • 日志默认走 JSON Lines,带轮转和过期清理,关键模式(password、token、key、credential、host、本地路径)会被脱敏。
  • 完整 host/path 只在用户显式开启的、带自动过期的诊断模式中临时记录。
  • 诊断导出包会再次脱敏本地路径、host、token、password、key 等,并不包含连接库、known_hosts 或秘密。

换句话说,你把诊断包发给别人帮忙排查,不会顺手把你的服务器地址、密码或私钥一起发出去。

这些边界是写进架构的,不是事后补的

ConchTerm 的安全模型不是上线前加的一层加密,而是从架构第 0 阶段就写进规则里的不变量:秘密不传播、路径不跨进程、IPC 全程校验、日志默认脱敏。这也是我们在 dogfood 阶段就愿意公开讨论这些边界的原因——它们是产品结构的一部分,而非营销话术。

相关事实源

本文内容可追溯到以下仓库文件:

  • ARCHITECTURE.md
  • docs/DESIGN.md
  • docs/generated/system-inventory.md