Files
lxc-streamutils/docs/security.md
T

5.1 KiB
Raw Blame History

安全模型

本服务同时处理 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,不提供全局订阅。
  • 同一 kind 的多个实例仍逐行执行 owner 归属校验;实例 token 不能订阅同账户的另一个实例。
  • 路由器按事件的可信 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 确认后才原子创建账户并消费邀请码。
  • 已登录账户可用当前 TOTP 或未消费恢复码开始短期 TOTP replacement enrollment。旧密钥在新动态码确认前保持有效;确认事务会更换密钥、轮换全部恢复码并撤销除当前会话外的其他网页登录会话。

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 后,旧浏览器源立即断开且无法重连。