Skip to content

security(credentials): 凭据存储实现与 Keychain 威胁模型不一致 #230

@appergb

Description

@appergb

背景

项目说明与 persistence.rs 文件头仍描述 Keychain-backed credentials,但当前实现实际上把凭据写入本地 JSON 文件。这个 issue 只跟踪 凭据存储策略与威胁模型不一致,不混入 QA XSS 或 provider status 修复。

现状

文档 / 文件头语义:

  • 凭据应存在 Keychain / 平台凭据存储。
  • plaintext JSON 应只是 Swift legacy fallback / 首次迁移入口。

实际实现:

  • macOS / Linux 写 ~/.openless/credentials.json
  • Windows 写 %APPDATA%/OpenLess/credentials.json
  • Unix 下设置父目录 0700、文件 0600
  • CredentialsVault 注释明确说“不走 Keychain”

代码证据

  • openless-all/app/src-tauri/src/persistence.rs:1-13:文件头描述 Keychain-backed vault
  • openless-all/app/src-tauri/src/persistence.rs:108-113:实现注释改为纯 JSON,故意不用 Keychain
  • openless-all/app/src-tauri/src/persistence.rs:236-252:凭据路径指向 JSON 文件
  • openless-all/app/src-tauri/src/persistence.rs:289-304:写入 JSON 并设置权限
  • openless-all/app/src-tauri/src/persistence.rs:658-664CredentialsVault 不再使用 Keychain,仅保留 service name 常量

风险

0600 / 0700 可以降低跨用户读取风险,但不等同于 Keychain / Credential Manager:

  • 同用户恶意进程、备份同步、误打包、日志/诊断收集路径更容易接触 plaintext 文件。
  • 一旦 WebView 注入或本地低权限链路能调用 app command,plaintext 存储会放大影响。
  • 文档与实现不一致会误导后续安全设计。

需要决策

二选一即可,不要长期保持“文档说 Keychain、实现写 JSON”的中间态:

  1. 恢复平台凭据存储,并把 JSON 变成只读一次性迁移来源。
  2. 明确产品选择 plaintext local file,并同步更新 AGENTS / docs / UI 文案 / threat model,同时减少前端读取 raw secret 的能力。

验收标准

  • 文档、文件头、实现三者对凭据存储策略一致。
  • 如果恢复 Keychain:首次启动可迁移 legacy JSON,迁移后不继续写 plaintext secrets。
  • 如果保留 JSON:文档明确说明安全边界,并保证文件权限、路径、备份/导出风险有清楚处理策略。
  • read_credential 这类 raw secret 返回路径经过重新评估,不默认暴露给所有窗口。

关联

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions