Skip to content
自托管密码管理器:实际需要付出什么

自托管密码管理器:实际需要付出什么 ​

自托管一个密码管理器,意味着由你自己来运行那个加密同步服务器。你的客户端在设备上加密;你运营的服务器存储密文并为你做身份验证。那台服务器一旦被攻破,落到对方手里的是一个加密的 blob,而不是一份密码清单。

这是一项真实且持久的隐私收益。它同时也是一份维护承诺,而本文诚实的版本会把两者都讲清楚。

自托管实际改变了什么 ​

这一点要说准确,因为期待出错的正是这里:

厂商云自托管
谁能读取你的保险库没有人,前提是零知识没有人,前提是零知识
谁能 删除 你的保险库厂商你
谁能看到元数据厂商你
谁可能被强制交出数据厂商,在他们的司法辖区你,在你的司法辖区
可用性责任厂商你
TLS、打补丁、备份厂商你
成本订阅费服务器 + 你的时间

机密性方面的结论并没有改变。改变的是 对存储平面的控制权,以及信任链中都有谁。自托管移除的是第三方;它不会增加密码学。

什么时候值得 ​

  • 你已经在运行一些服务,并且有 NAS、家庭实验室或一台小型 VPS。
  • 你的威胁模型里包含「服务商被攻破或被胁迫」。
  • 你所在的司法辖区里,别人的数据托管对你是一种风险敞口。
  • 你想为一个团队在你审计过的基础设施上提供共享集合。
  • 你是那种享受五分钟 Docker Compose 加一个 cron 任务的人。

什么时候不值得 ​

  • 你从未运行过反向代理,需要先学 TLS、DNS 和防火墙规则。
  • 不会有人记得打补丁。未打补丁的服务器是一种风险敞口,而不是安全上的胜利。
  • 你是唯一用户,只在一台设备上。那就完全跳过服务器,用本地保险库。
  • 你对一个业务关键系统需要保证的可用性,却没有任何备份计划。

对于后两种情况还有一条中间路线:一个 本地加密保险库,配合用于你自己设备的 Nearby 局域网同步,完全不要服务器。

五分钟搭起来 ​

OpenKey Server 是一个带 PostgreSQL 的 FastAPI 应用,以 Docker Compose 栈的形式提供。

bash
cd openkey_server
cp .env.example .env
openssl rand -hex 32      # paste this into JWT_SECRET in .env
docker compose up --build -d

然后确认它状态健康:

URL用途
http://localhost:8000API 基址
http://localhost:8000/docsOpenAPI 文档
http://localhost:8000/health健康检查

架构迁移会在启动时自动运行。从 设置 → 数据 → 自托管服务器 连接一个客户端,然后在第一台设备上 注册,其余设备 登录。完整细节见服务器设置。

加固清单 ​

这是人们会跳过的部分,而它恰恰决定了自托管究竟有没有帮上忙。来自安全指南:

不可妥协 ​

  1. 一个至少 32 个字符的唯一 JWT_SECRET。 占位符值会在启动时被拒绝。自己生成一个;不要照抄示例。
  2. 带有效证书的 HTTPS。 客户端使用平台 TLS 栈且没有证书固定,因此一个拼错的 http:// URL 或一张坏证书,就会在登录和同步时开启中间人攻击。请在 Caddy、nginx 或你的负载均衡器上终结 TLS。
  3. 明确的 CORS_ORIGINS 允许列表。 绝不要用 *。如果你使用浏览器扩展,请显式加入它的 chrome-extension:// 和 moz-extension:// 来源。
  4. Postgres 和原始 API 端口保持私有。 只暴露反向代理。
  5. 在代理上设置 HSTS,这样浏览器在首次访问后就绝不会回退到 HTTP。

强烈建议 ​

  1. 在反向代理上做速率限制。 API 内建的限流器是内存中的,且 按工作进程 计算,所以在有多个工作进程或副本时,实际生效的上限会成倍增加。在 nginx 中加 limit_req,或在边缘加上 Caddy 速率限制。
  2. 仅当代理会覆写 X-Forwarded-For 且你信任该路径时,才设置 TRUST_PROXY_HEADERS=true。否则你的按 IP 限制会作用在代理上,而不是用户身上。
  3. 警惕邮箱枚举。 POST /auth/prelogin 和 POST /auth/lookup-public-key 对未知邮箱返回 404,这有助于合法客户端,但也让人可以探测哪些地址已注册。请使用严格的速率限制和 TLS;对高敏感部署,还可选择使用 VPN 或 IP 允许列表。
  4. 备份 Postgres 并测试恢复。 一个从未从备份中恢复过的密码管理器服务器,只是一个假设。
  5. 监控 /health 和 API 日志;一旦它停止响应就告警。

自托管同步服务器能做什么、不能做什么 ​

它能它不能
通过你的 auth_hash 验证你的身份读取你的主密码
为条目、附件、组织和共享存储不透明密文解密集合名称或条目载荷
删除或扣留你的数据恢复一个被遗忘的主密码
看到元数据:邮箱、密文大小、时间规律从数据库中重建你的保险库
由你来限流、打补丁或重启在你忘记主密码后还能撑住

仔细读第四行:自托管服务器 不会 让你更能免于忘记密码的风险。它把一方从信任链中移除,同时把你加进来,成为那个可能丢失数据的运营者。忘记主密码 讲的是如何预防这一侧。

在依赖它之前你该知道的同步语义 ​

  • 按每项 revision 最后写入获胜,而不是 CRDT。 两台设备上的并发编辑可能互相覆盖。重要时请一次只在一台设备上编辑。
  • 删除会以墓碑形式同步,直到对端追上,因此删除在各处都不是立即生效的。
  • Nearby 局域网同步使用同样的最后写入获胜规则,在已配对、已链接保险库的设备之间。
  • 两者都不是备份。 至少保留一份加密的本地备份(OpenKey 中为 .okbak)。

最后这一点是最常被人弄错的,而它正是「我把保险库搬到了自己的服务器」和「我有一套恢复方案」之间的区别。

面向运营者的运维安全 ​

  • 把服务器运行在一台你有固定打补丁计划的主机上。未打补丁比交给厂商托管更糟。
  • 把 JWT_SECRET 放在密钥管理器中,或至少放在权限为 600 的文件里,而不是你的 shell 历史中。
  • 日志保留是一种风险敞口:同步日志可能暴露时间规律和数据大小。轮转它们并设置上限。
  • 备份 数据库,而不只是卷,并每季度验证恢复。
  • 绝不要在没有 TLS 的不受信网络上暴露 API。
  • 如果你需要可用性保证,就在负载均衡器后面放第二台主机,并接受同步冲突解决仍然是最后写入获胜这一事实。

OpenKey 如何让服务器对攻击者毫无用处 ​

  • 客户端用 Argon2id 从邮箱 + 主密码 + 盐派生主密钥。
  • 登录会发送一个 auth_hash,它在证明知识的同时不泄露密码。
  • 一把 保险库密钥 用 AES-256-GCM 加密集合名称和条目载荷。服务器只存储被包装的保险库密钥。
  • 访问 JWT 生命周期很短;刷新令牌在静态存储时被哈希,并在使用时轮换。
  • 附件以密文同步,每个上限 20 MB。

一份被盗的 postgres 转储只会给攻击者带来盐、KDF 参数、包装密钥和 blob。破解它意味着要攻击 Argon2id,而即便如此,他们手中的仍是没有密钥就读不懂的密文。这就是全部的安全论证,而它之所以成立,恰恰是因为主密码从未离开过客户端。

搜索数据说明 ​

自托管是一个规模不大但真实存在的查询簇,而且它更多是围绕「open source」这个说法聚集,而非围绕「self-hosted」。Google Trends(全球范围,过去 12 个月)将这些词互相比较:

查询簇内相对关注度
passbolt100
open source password manager55
password manager self hosted13
self-hosted password manager4
keepass alternative1

这意味着「open source」是人们首先想到的说法,而「self-hosted」是他们随后落脚的说法 — 一次以偏好开始、以实现方式结束的搜索。「open source password manager」下的相关查询指向同一方向:KeePass 为 100,「open source password manager self hosted」为 84,Passbolt 为 78。前三名中有两个是人们正在比较的产品名,而不是某项功能的描述。

与头部词条相比,这些数字仍然很小。「Self-hosted password manager」是小众中的小众,而诚实的结论是:搜索这个短语的大多数人,是那些已经清楚自己想要什么的技术用户。

Method: Google Trends,全球范围,过去 12 个月,2026 年九月提取。数字为归一化的相对关注度(0–100),而非搜索量。

五分钟版本 ​

自托管是在信任链中用你自己替换掉服务商。它不会增加密码学,它给你的清单上增加的是 TLS、备份、打补丁和速率限制。如果你已经在运行服务:Compose 栈、唯一的 JWT_SECRET、带有效证书的 HTTPS、明确的 CORS_ORIGINS,以及经过测试的备份。如果你没有,则改用本地加密保险库加 Nearby 局域网同步,并且无论哪种方式都至少保留一份加密的离线备份。

下一步 ​