Files
WebDrop/docs/threat-model.md
justme 12980ba6fe
ci/woodpecker/push/web-build Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/push/dev-image Pipeline was successful
docs: describe login-only spaces and the 8.0.0 contract [CI]
2026-10-16 06:46:16 +08:00

7.4 KiB

威胁模型

1. 信任边界

角色 是否受信
用户浏览器中运行的前端代码 受信(信任根,见 §5 的残余风险)
后端进程、其内存、磁盘、日志、核心转储 不受信
反向代理、CDN、TLS 终结点、负载均衡 不受信
网络路径 不受信
持有链接的成员 对频道内容受信,对管理权不受信

设计目标:即使整个服务端被攻击者完全控制,攻击者也读不到内容和文件名,并且无法在不被发现的情况下篡改、重排、截断或替换内容。

2. 服务端能看到什么

能看到 说明
频道 id、模式、设置 包括共享频道名、成员人数上限、权限、TTL、下载上限等,服务端需要据此执行
帧数量、时间、序号 追加顺序由服务端分配
密文大小 经过档位 padding,见 protocol §6
帧标志 FINAL、SNAPSHOT、GATED、ENC
条目 id、帧序号 随机值,与内容无关
令牌的哈希、终端的哈希 不是原值,但每次请求中会短暂看到令牌原值
下载次数 服务端计数
传输层信息 IP、TLS 指纹、访问时间
账号服务可见 用户名、口令 verifier、保险库密文大小和更新时间;不会看到保险库明文、频道密钥或分享链接
业务结构和个人资料 账号昵称头像、空间和频道名称、分组、文件夹名称、资源归属、资源作者;由后端管理,不属于资源内容的端对端加密承诺
看不到(enc 模式) 说明
内容 文本、JSON、文件字节
文件名、类型、总大小、文件内设备标签 都在加密的条目元数据里
密钥 K、管理密钥 A 由客户端保管,以账号公钥封装的密文可存服务器;可选分享链接使用 fragment

raw 模式下服务端能看到全部负载,界面必须明确提示。

3. 令牌的含义

令牌(T_read / T_write / T_owner)是授权凭证,每次请求都会经过服务端,所以恶意服务端可以截获它们。这不会导致内容泄露:

  • 令牌由 K 经 HKDF 单向派生,不同用途用不同 info 标签,令牌无法反推 K 或 K_enc。
  • 令牌只授权访问密文,而密文本来就在服务端手里。
  • 截获 T_owner 的恶意服务端可以改设置、删除内容,影响的是可用性,不是机密性或完整性。

4. 攻击与对策

攻击 对策
服务端篡改帧内容 AES-GCM 认证,失败即拒绝
重排、丢弃中间帧 帧序号在 AAD 中,接收方要求 0..n 连续
截断条目 FINAL 标志在 AAD 中,没收到已认证的 FINAL 帧就标记为不完整
把帧搬到其他频道或条目 AAD 绑定 channel_id 和 item_id
翻转 FINAL 或 SNAPSHOT 标志位在 AAD 中
模式降级(enc 换成 raw) 链接带 k 就只接受 enc 且认证通过的帧,不匹配直接拒绝
重放旧帧 同一条目内 (item_id, frame_index) 唯一,服务端幂等,客户端按条目去重
服务端隐瞒或删除条目 无法防止(可用性),见 §6
链接泄露 链接即能力。成员人数上限限制账号成员数,频道只接受登录账号,所有者可撤销成员和删除频道
fragment 外泄 取出后立即 history.replaceState;Referrer-Policy: no-referrer;不把 K 放进查询串
XSS 严格 CSP;条目预览在沙箱中渲染;raw 内容不内联渲染,下载带 attachment 和 nosniff
暴力猜频道 id id 128 位随机
撞库/截获账号登录 客户端 PBKDF2 派生登录凭据,经 HTTPS 传输;服务端另存独立随机 salt 的 PBKDF2 慢哈希,并限流。TLS 或运行终端泄露仍可能暴露可登录凭据
账号数据库泄露 数据库只有加盐慢哈希,不能直接用于登录;弱口令仍可被离线猜测。登录凭据不能直接派生保险库密钥
账号混用/设备身份混用 按账号隔离 IndexedDB,切换时中止后台任务;保险库不包含设备 S,每台设备单独生成
绕过频道加入规则 服务端检查 owner/member 角色、邀请、密码、账号 session(没有则一律拒绝)、成员人数;加密选项不会绕开权限
匿名滥用与 DoS 创建时的 Cap 验证码(工作量证明由 Cap 提供)、限流、频道配额、条目数和字节数上限

5. 前端信任根(残余风险)

邀请密文使用收件账号的 P-256 公钥派生 AES-GCM 密钥,私钥在密码保护的保险库中。但首次邀请公钥来自服务端目录,目前没有外部指纹核验:恶意服务端替换公钥会影响新邀请的保密性。此限制与已有频道内容的密钥/帧认证边界不同。

端到端加密的前提是浏览器执行的是正确的代码。如果同一个被攻破的后端同时下发前端,它可以换成偷 K 的页面。

隔离部署的缓解:

  • 前端静态资源托管在独立于 API 的域名,API 节点被攻破不影响前端代码。
  • 可由独立托管方校验脚本 SRI 与构建哈希;前端托管方被攻破的风险仍然存在。

嵌入式部署的取舍:

  • WebDrop.Frontend 将 Vite dist 嵌入后端程序集,让 API 以同源方式提供页面。
  • 这只简化部署,不增强前端与后端的隔离。若部署主机或镜像被控制,攻击者仍可替换页面并窃取用户后续输入的密钥。

后续:发布签名加 PWA 校验、CLI 客户端(协议不依赖浏览器,第三方可自行实现)。

6. 不做的事

  • 不隐藏 IP 和访问时间。需要的话用 Tor 或自己的代理。
  • 不阻止成员保留明文。复制和下载发生在浏览器里,服务端只能限制拉取密文的次数。「界面禁止复制或下载」只是界面约束。
  • 不防服务端的可用性攻击:隐瞒、删除或拒绝服务。
  • 不提供前向保密:持有链接的人可以解密该频道的全部历史密文。保护手段是删除密文和 TTL。
  • 不提供不可否认性:帧没有逐发送者签名,所有成员共享同一密钥。
  • 不防已被攻破的终端。
  • 不做口令叠加、后量子,留待后续版本。
  • 账号密码不是频道访问控制。账号保险库只同步持有的资源链接;拿到频道链接的人仍按频道自身能力访问。
  • 无密码重置。忘记密码会失去保险库解密能力;未来若需要找回,须增加独立恢复密钥,而不能由服务端重置后解密旧保险库。

7. 服务端内存与日志的要求

  • 服务端代码路径中不出现明文和 K,也没有解密函数。
  • 令牌只做哈希比对,使用常量时间比较,不写日志。
  • 日志不记录请求头、请求体、完整 URL;频道 id 在日志中截断或哈希。
  • 错误信息不回显令牌和任何用户提交的字节。

8. 账号设备与本机缓存

同一账号的各设备拥有独立 sessions,但在频道成员上限中只计一个账号成员,成员请求凭账号 session 鉴权。撤销设备会删除该设备 sessions;不会移除账号成员或释放其成员名额,也不会影响其他设备。撤销不能擦除已下载数据、已持有的频道 K 或被复制的账号私钥。当前未实现密钥轮换,不声称前向保密。未登录访客无法跨设备去重,每个访客终端单独计入人数上限。

本机记住登录允许本浏览器使用者访问账号,退出时删除自动恢复凭据。OPFS 存储解密后临时文件,正常退出账号时清理;浏览器崩溃可能留下本机缓存,它不属于服务端密文。