Files
lxc-streamutils/docs/security.md
T
felis f79852d8e6 add account localization and switchable rooms
Centralize control and OBS copy in a shared TOML catalog, persist the selected locale per account, and broadcast language changes to component streams. Allow account owners to atomically switch their Bilibili room and restart the shared listener without changing component URLs.
2026-07-18 23:28:05 -07:00

79 lines
4.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 安全模型
本服务同时处理 Bilibili 登录 Cookie、TOTP Secret、一次性恢复码、管理员邀请码和 OBS
token。以下规则是实现约束,而不是可选部署建议。
## 租户隔离
- HTTP handler 只从服务端会话解析 `owner_id`,不接受客户端声明的 owner。
- 组件查询始终限定 `owner_user_id`;组件表不保存直播源外键。
- 直播间、直播源和 CookieCloud 凭据分别以账户 ID 建立唯一约束与 RLS 边界。
- 用户切换直播间时,账户房间、直播源房间与加密 CookieCloud 凭据在同一事务中更新;目标房间仍保持账户间唯一。
- 数据库使用 owner 复合外键、RLS 和 `FORCE ROW LEVEL SECURITY`。
- 语言偏好使用强制 RLS 的账户行;修改时只向该 owner 的组件广播 locale。
- tenant 查询必须在事务中执行 `SET LOCAL app.user_id`,不能使用会泄漏到连接池的 session-level
`SET`。
- 实时广播按 `component_id` 建立独立 channel,不提供全局订阅。
- 路由器按事件的可信 `owner_id` 扇出,并在投影发布前再次验证 owner、账户 source 和 component ID。
## Secret 生命周期
| Secret | 浏览器可见性 | 数据库存储 | 轮换/消费 |
| -------------------- | ------------------- | ----------------------- | ---------------- |
| TOTP Secret | 注册时显示一次 | XChaCha20-Poly1305 密文 | 账户绑定 |
| 恢复码 | 注册完成时显示一次 | SHA-256 摘要 | 单次消费 |
| 登录 session | HttpOnly Cookie | SHA-256 摘要 | 到期、登出或撤销 |
| CookieCloud Key/密码 | 用户提交时 | XChaCha20-Poly1305 密文 | 覆盖更新 |
| 邀请码 | 创建时显示一次 | SHA-256 摘要和前缀 | 单次消费或撤销 |
| OBS token | 创建/轮换时显示一次 | SHA-256 摘要 | 组件级轮换 |
`security.data_encryption_key`
是恢复密文所必需的主密钥。它必须独立备份,但不能提交到 Git 或写入镜像。
## Passwordless 认证
- 第一个系统管理员只能通过数据库为空时的 bootstrap 流程创建。
- bootstrap proof 不成为账户密码,也不能用于日常登录。
- TOTP 接受有限时钟偏移,并持久化最近使用的 time step,阻止同一码重放。
- 登录与匿名注册同时按账户维度和网络维度限流,错误消息不暴露用户名是否存在。
- 注册先写入短期 pending enrollment;只有正确 TOTP 确认后才原子创建账户并消费邀请码。
## CookieCloud 与 SSRF
- 部署者通过 `cookiecloud_allowed_hosts` 指定精确的基础地址白名单。
- URL 会规范化,并拒绝 embedded credentials、query 和 fragment。
- Key 编码成单一路径段,不能注入额外路径。
- HTTP 客户端禁止重定向,防止允许的地址跳转到内网目标。
- 浏览器只看到 host 和 `keyConfigured`/`passwordConfigured`,不会读回凭据。
- `libilibili::Credentials` 的 `Debug` 已脱敏,但应用仍不得记录 Cookie、原始认证响应或弹幕 token。
## HTTP、WebSocket 与 OBS
- 公开部署必须在受信任反代终止 HTTPS,并保持 `secure_cookies = true`。
- 生产 session 使用 `__Host-` Cookie、HttpOnly、Secure 与 SameSite 策略。
- 写 API 执行 same-origin 检查并限制 body 大小。
- 所有 `/api/*` 响应使用 `Cache-Control: no-store`。
- OBS token 放在 URL fragment,并作为 WebSocket 第一帧发送。
- token 只具有 `events:subscribe` scope,且只能订阅其绑定的组件。
- WebSocket 首帧、frame size、认证时间和全局连接数均有上限。
## PWA 边界
- manifest、Service Worker 和 start URL 都限定在 `/control/`。
- `/obs/*` 不在 Service Worker scope 内。
- worker 只缓存静态应用壳层、图标和构建资源;不缓存 API、WebSocket 或直播事件。
- 离线 mutation 直接失败,不使用 Background Sync。
- 新 worker 仅在用户确认后激活。
- TOTP、恢复码、邀请码、新 OBS 地址或未保存设置可见时,更新会被阻止。
## 上线检查表
- [ ] 使用独立随机 `data_encryption_key`,并在安全位置备份。
- [ ] `config.toml` 权限为 `0600`,且已被 Git 和 Docker build context 排除。
- [ ] PostgreSQL runtime role 可以执行迁移,但 PUBLIC 无权执行 SECURITY DEFINER helper。
- [ ] `secure_cookies = true`,Nginx 正确传递 `X-Forwarded-Proto https`。
- [ ] 应用仅监听 loopback,9719 未直接暴露公网。
- [ ] CookieCloud 白名单只包含管理员批准的实例。
- [ ] 日志中没有 Cookie、TOTP、token、邀请码或上游认证响应。
- [ ] 轮换 OBS token 后,旧浏览器源立即断开且无法重连。