Haven 安全机制全解析:bcrypt、JWT、MFA、限流与磁盘守护的 5 道防线

原创2026-10-28 21:14:022,174 阅读

Haven 安全机制全解析:bcrypt、JWT、MFA、限流与磁盘守护的 5 道防线

Haven 是一款自托管(self-hosted)私有聊天服务器,可部署在自己的机器上作为 Discord 替代品,主打"无云、无遥测、无大科技公司"。但数据放在自己手里,安全设计是否可靠就成了关键问题。本文带你从源码层面快速看懂 Haven 的 5 道核心安全防线:bcrypt 密码哈希、JWT 会话管理、TOTP 双因素认证、多层限流,以及独特的磁盘守护机制,帮助你在部署前心里有底。

Haven 自托管聊天服务器登录界面与安全设置总览

1️⃣ 密码存储:bcrypt 哈希,数据库泄露也安全

Haven 用 bcrypt 对用户密码做哈希存储,而不是保存明文——即使整个数据库被偷走,攻击者也拿不回你的密码。几个值得注意的细节:

  • 强度分级:注册时密码用 12 轮 cost 因子哈希,兼顾抗破解能力和登录速度;
  • 防"用户名探测":当登录的账号不存在时,服务器会拿一个假哈希(dummy hash)执行一次等时长的比对,让"报错速度快慢"无法暴露哪些用户名真实存在;
  • 密码策略:密码必须 8–128 位,且不能包含自己的用户名;
  • 临时密码机制:管理员重置密码后,用户可用原密码登录来"取消重置",这样其端到端(E2E)私钥的包装密钥不受影响,DM 历史得以保留(见 src/auth.js)。

2️⃣ 会话管理:JWT 令牌 + password_version 撤销

Haven 的登录态基于 JWT(JSON Web Token),令牌由环境变量 JWT_SECRET 签名,未设置该密钥时服务器会直接拒绝启动(src/auth.js)。

无状态令牌有个经典难题:如何"作废"一个被盗的令牌?Haven 的答案是 password_version 机制:

  • 每个令牌都携带 pwv(密码版本号)声明;
  • 修改密码、启用 2FA、注销其他设备时,服务器把账号的 password_version 加一;
  • 所有旧令牌的 pwv 与数据库不一致后立即失效,5 秒缓存窗口内完成全局生效(verifyToken)。

管理员还可以配置会话时长(0–365 天,0 表示永不过期);访客账号则只有 12 小时短会话。

3️⃣ 双因素认证:TOTP 动态码 + 一次性备份码

Haven 内置标准 TOTP(Time-based One-Time Password)双因素登录(src/auth.js),兼容 Google Authenticator 等主流验证器应用:

设计点 说明
6 位动态码 / 30 秒 标准 TOTP 参数,SHA1 算法
挑战令牌 密码通过后先签发 5 分钟有效的 challenge token,输入动态码后才发正式会话
动态码只许用一次 服务器记录已用过的时间步,防止偷看到的 6 位码在一分半钟内重放
8 个备份码 启用时生成 XXXX-XXXX 格式的一次性备用码,数据库里只存 SHA-256 哈希
错误锁定 每账号 15 分钟内错 10 次动态码即暂停,防 6 位码被暴力穷举

开启和关闭 2FA 都必须重新输入密码,防止被盗会话被"偷梁换柱"。另外还有账号恢复码:提前生成 8 个一次性恢复码,丢失密码和 2FA 时也能自助重置(src/auth.js)。

Haven 聊天界面:成员列表、角色与频道展示

4️⃣ 限流:从 IP 到账号的四层闸门

Haven 的限流全部在内存中实现,零额外依赖,且层层递进(src/auth.js):

  1. IP 级认证限流:同一 IP 每 15 分钟最多 20 次登录/注册请求,超出返回 429;
  2. 账号级密码错误限制:同一账号 15 分钟错 20 次,且同一 IP 错 10 次也会被挡——但"自己用过的 IP"只受较宽松的账号级限制,避免陌生人撞库反而把房主锁在门外;
  3. 全局注册速度限制(可选):限制服务器每小时新增账号数上限,专门对付"分散到大量 IP"的机器人注册潮,这类攻击恰恰绕得过 IP 级限流;
  4. CAPTCHA(可选):集成 Cloudflare Turnstile,服务端在线验证令牌真伪,伪造或网络异常一律视为失败。

此外,IP 封禁功能自带"防误伤":服务器会自动拒绝封禁本机、封禁操作者自己、或封禁管理员/同级成员所用地址(src/ipBanGuard.js)。

5️⃣ 磁盘守护:让服务器永远不会"卡死在满盘"

这是 Haven 最有意思的设计之一(src/diskGuard.js)。

问题:SQLite 删除一行数据时本身也要写入磁盘。一旦数据卷被文件塞满,管理员连"删掉占空间的附件"都做不到——删除操作本身写不进去,实例彻底卡死,只能扩容硬盘解围。

解法:永不交出最后一部分空间。

  • 默认保留 512 MB(可用 HAVEN_DISK_RESERVE_MB 调整),专留给数据库、WAL 和 vacuum 操作;
  • 上传守卫 guardUploads 在文件落盘之前检查空间,一旦剩余空间触及保留线,直接返回 507 暂停上传,保证"删除"这条路永远走不通;
  • 检测用 statfs 系统调用,5 秒缓存避免突发上传时频繁系统调用;
  • 平台不支持时"失败开放"(fail open),与没有该功能前行为一致;
  • 空间紧张时,控制台每 5 分钟告警一次,同时把状态推给管理端界面,管理员无需盯日志也能发现。

6️⃣ 更多细节:SSRF 防护与出网检查

  • 安全出站抓取:链接预览、媒体代理等功能会代服务器去抓用户指定的 URL,src/safeFetch.js 对每一跳重定向做 DNS 解析并核对地址,阻断"让用户链接把服务器指向内网"的 SSRF 攻击;
  • SSO 头像下载:校验协议、MIME 类型和文件魔数,下载后还要经过隐私清洗(去除 EXIF 定位信息)才能入库;
  • 漏洞上报:发现安全问题请通过 SECURITY.md 中的私有渠道报告,只有最新版本会获得安全修复。

小结:一张表看懂 Haven 的 5 道防线

防线 机制 核心文件
密码存储 bcrypt 哈希 + 防探测假哈希 src/auth.js
会话管理 JWT + password_version 全局撤销 src/auth.js
双因素 TOTP + 一次性备份码 + 恢复码 src/auth.js
限流 IP / 账号 / 全局注册四层闸门 src/auth.js
磁盘守护 512 MB 保留空间,拒绝"吃掉逃生通道"的上传 src/diskGuard.js

自托管的最大吸引力是数据自主,而 Haven 在密码学、会话生命周期和运维容灾上的这套组合拳,正是"把服务器放到自己手里"能让人放心的底气所在。

登录后查看全文
Haven