在 2012 年初,我曾写过一篇《我的密码管理方法》,介绍了 KeePass 这一密码管理器。在 2019 年,我从 KeePass 生态迁移到了 1Password 生态。
一、再谈密码管理器的重要性
拖库/撞库的危害,以及每个服务使用独立密码的重要性这里就不再赘述了。要想达成每个「每个服务使用独立的密码」这一目标,常见的做法有:
- 「词根法」,即构建一个复杂的密码,再加上各网站的域名。比如 Google 的密码是
hunter2$google.com
、Yahoo! 的密码是hunter2$yahoo.com
这样; - 「哈希法」,即用固定字符串(盐)和网站域名通过某些哈希函数(比如 HMAC)派生出密码。比如 Google 的密码由
hash('hunter2', 'google.com')
派生出585ff1caa5
、Yahoo 的密码由hash('hunter2', 'yahoo.com')
派生出d749f97459
这样; - 使用密码管理器,为每个网站生成一个与网站无关、完全随机的字符串作为密码。
在过去的这些年里,我在不同的场合向未使用密码管理器的人推荐密码管理器时,多次遇到过「词根法」和「哈希法」的拥趸,认为这两种方法非常安全,所以不需要密码管理器或随机密码。
事实并非如此。对于大规模泄露来说,别有用心的攻击者很容易就能看破「词根法」的小伎俩。「哈希法」虽较「词根法」稍强,但是你真的能记住那些不常用网站的生成参数吗?比如 Gmail 的密码是用 google.com
生成的还是用 gmail.com
生成的?Dropbox 的域名由 getdropbox.com
改成 dropbox.com
之后我改过密码了吗?这个很久没登录过的论坛我曾经注册过吗?登录邮箱是哪一个?
另一个问题在于密码轮换(password rotating)。密码轮换是否对安全性有提升这一点我们暂且不论,但某些服务(尤其是金融合规方面)的确是强制密码轮换的。在密码轮换机制面前,人脑很快就记不住这个服务的密码是第几次轮换了。
密码管理器 + 完全随机密码可以避免以上问题,在管理大量网络账号与身份时不可或缺。
二、安全性与便利性的博弈
自 2012 年初开始使用 KeePass 生态之后,我使用过 KeePass, KeePassX, KeePassXC, KeePassDroid, Keepass2Android 等多种 GNU/Linux 与 Android 平台的客户端。
出于某些守旧思想,我一直没有配置这些东西与其他应用程序的整合。无论是在计算机上还是在手机上,我要登录某个网站或应用,都是打开密码管理器,搜索找到相应的条目,然后复制用户名、密码,粘贴到对应的网页、应用里。日久天长,这些操作我已经可以闭着眼睛完成,以至于某次 KeePassXC 更新之后我的常用的快捷键失效了,我恼怒异常。这一流程更大的问题在于这些密码会在剪贴板中停留一段时间,可以被剪贴板管理器等其他程序读取,这无疑是一个安全风险。
另一个更加不便的问题在于同步。2012 年时我只有一台常用电脑和一只常用手机,而现在我有三台常用电脑和两只常用手机。各设备之间的 Dropbox 同步就显得愈发痛苦了。由于 KeePass 的数据库是单个二进制文件,Dropbox 无法分辨这个文件改动了什么(也不应该能分辨),因此在两个设备同时修改数据库中的条目的时候必然会出现同步冲突。每次遇到同步冲突,我就得把冲突的两份数据库文件导出成 XML 手动进行比对和合并(我不敢使用那个 Merge Database 的功能),而且往往会发现两边其实只是各新增了一个条目,两个条目完全不相关。如果是一个更加智能的同步系统的话,显然是不会出现同步冲突的。
因为守旧思想,我不愿意使用浏览器整合,但是为了便利性,我又使用了 Chrome 的保存密码功能。这一功能后来进化为了 Google Passwords,除了 Chrome 也可以在 Android 应用中保存和填写密码。然而由于 Google Passwords 过于简陋,会把同一网站的密码(在不同页面)保存多份,在增加便利性的同时也增加了我的密码管理体系的混乱度。
我也曾尝试迁移至别的密码管理器。我曾经考察过 1Password,但是当时它并没有 GNU/Linux 客户端,所以一直没做考虑。公司有 1Password Team 的订阅,因此我对它的网页版还是相当熟悉的。在上述 KeePassXC 快捷键失效事件之后,我终于决定再次调查一下迁移密码管理器的事情。然后我发现 1Password 现在有个基于浏览器扩展的客户端——1Password X。
用了一段 1Password X 和 1Password for Android 之后,我感叹——真便利啊!
三、1Password.com 与 1Password X
多年前我首次听说 1Password 的时候,它并没有 GNU/Linux 可用的客户端。当时它的各客户端是单独售卖,用户可以通过 Wi-Fi、Dropbox 或是 iCloud 在各设备之间进行同步。近两年,1Password 改变了盈利模式,开始主推 1Password.com 这一订阅制服务。订阅了 1Password.com 会员的用户无需再为客户端付费,而是把(本地加密后的)数据借助 1Password 的服务器在各设备之间同步。
1Password 很早以前就是有个叫 1Password extension 的浏览器扩展的,但是那个扩展是依赖本地(Windows 或 macOS)应用程序的。而 1Password X 是较新的(看更新日志应该是 2017 年 6 月出了首版),不依赖本地应用的扩展。1Password X 完全运行于浏览器之中,与 1Password.com 进行通信,因此可以在 GNU/Linux 中使用。
与 KeePass 生态这样「DIY 同步」不同,1Password X 必须要用使用 1Password.com 的同步服务。1Password X 的数据存在于 1Password.com 上,但是 1Password.com 的员工并不能解密你的数据。无论是 1Password X 还是 1Password.com 的网页版,本质都是 JavaScript 客户端,将明文数据加密之后再上传至 1Password.com。这一点可以在浏览器控制台中进行观察。
1Password.com 这样的设计保证了哪怕 1Password.com 被黑客入侵,黑客也无法拿到用户的明文数据——因为 1Password.com 的服务器上根本没有解密用的密钥。每个用户的密钥是由注册时的 Secret Key + Master Password 通过 PBKDF2 生成的。Secret Key 即是注册时(本地浏览器中)生成的以 A3-
开头的一长串字符。由于 Secret Key 和 Master Password 都是 KDF 的原料,而 1Password.com 根本不存这些东西,因此丢失了任何一个的话,1Password.com 并不像普通网站一样提供「忘记密码」之类的按钮——你的数据再也无法被解密了。不过,如果你是在家庭版或团队版的成员的话,倒是有个挽回方法,下文详述。
1Password X 与 1Password.com 密不可分。比如要登录,其实就是弹个 1Password.com 的页面给你登录;要编辑某个条目,也是会弹一个 1Password.com 的页面给你编辑。1Password X 本身也能对条目对微小的修改:
- 点击那个 QR 码的图标,会在当前页面寻找并识别 TOTP 的配置码,并将识别出的 TOTP 密钥添加到对应的条目里;
- 点击那个 ☆ 的图标,能把当前条目加到 Favorites 里;
- 注册新服务或更改现有服务的密码的时候,可以直接新增或修改现有条目而不用打开 1Password.com 的页面。
四、导入数据
如前所述,我的密码主要保存在 KeePassXC 中,但是有一部分常用的服务,因为在 Chrome 中登录,因此其密码在 Google Passwords 里也存有一份。在 1Password.com 上可以直接选择导入从 Chrome 里导出的 CSV 密码文件,我就先拿这部分数据试了一下。效果挺好的,用网站域名作为条目名我很喜欢。然而由于 Chrome 会把同一个网站保存多份,其实脏数据很多。我点了 Undo Import 撤消了这次导入,将 CSV 文件去了一下重再导入。
由于 Chrome 的限制,目前 Chrome 里 1Password X 的密码提示会和 Chrome 自己的提示会同时出现,哪怕把 Chrome 自带的密码管理功能关闭也是如此。既然这些数据已经被整理到 1Password 里了,我就决定把 Google Passwords 里的数据删除了。一开始我没找到批量删除的按钮,只有一条一条删除,删了几百条之后浏览器重启了一次,全部同步回来了!这才想到去搜索一下,发现其实删除所有密码的入口在深藏在「清除浏览记录」里……
这样用了几天,感觉良好。便想着把 KeePassXC 里的数据也都导入了。1Password 并没有直接导入 KeePassXC 的功能,但是明确写了它能读取的 CSV 格式是如何的。将 KeePassXC 导出的 CSV 文件用 Python 或是各种电子表格软件调整一下列的顺序,就可以导入 1Password 了。
我在导入数据前先新建一个临时 vault,这样在导入之后方便整理和去重(Watchtower 里有重复密码检测的功能,如果每个网站使用独立密码的话,这里不应该有重复),将整理好的条目移入主 vault,最后再把这个临时 vault 删除。这样做还有个好处是条目的历史记录比较干净。
五、家庭版与团队版
1Password.com 个人版的账号是 2.99 USD/mo,而家庭版是 4.99 USD/mo,后者最多五人共享。很显然,只要有两人或以上使用,家庭版就是比个人版划算的。如果你像我一样爱上了 1Password 的话,可以邀请你的伴侣和家人一起使用 1Password。
1Password 的家庭版和团队版有个神奇的特性:如果有成员丢失了 Secret Key 或是忘记了 Master Password 的话,家庭/团队管理员是可以帮他恢复数据的。这很违反直觉:如果每个人的 vault 是用自己的 Secret Key + Master Password 加密的话,管理员是如何帮别人恢复数据的?在读了 1Password Team 的安全设计白皮书之后我才明白,原来在家庭版和团队版里,数据的加解密方式与个人版略有不同:
- 成员的数据并不是在密码学上只有自己能解密,而是家庭/团队管理员也可以解密;
- 如果有多个家庭/团队管理员的话,密码学上多个管理员之间能互相解密;
- 数据恢复过程受 1Password.com 的监督,只有 1Password.com 验证了成员的电子邮箱之后,才会允许家庭/团队管理员对其进行数据恢复操作;
- 如果有人同时控制了家庭/团队管理员和 1Password.com 的服务器,理论上是能用管理员的密钥解密其他成员的数据的;
- 如果家庭/团队管理员能访问成员的邮箱,那管理员也能获得成员的数据。
因此,如果你是极端被迫害妄想症,请不要使用家庭/团队版。当然,1Password 这种东西本来就不适合陌生人之间「拼团」——毕竟,「户主」是可以单方面删除家庭成员的整个账号的……
六、有趣的 k-anonimity
1Password 有个 Watchtower 功能,可以检查你有没有弱密码或是密码复用等问题。像我这样从另一个密码管理器迁移过来的肯定没这些问题,但是如果你之前使用过人脑生成密码、保存在 Chrome 中并导入了 1Password 的话,Watchtower 功能可能会帮到你。这些功能同样都是在浏览器中本地实现的。
Watchtower 中有个有趣的功能叫 Vulnerable Passwords,可以检查你的密码是不是曾经被泄露过(用的是 HIBP 的数据)。要检查密码有没有泄露过,岂不是要把明文密码或是哈希密码发给 1Password / HIBP?如果密码泄露过,那 1Password / HIBP 或是链路上的其他人(通过 MITM)不就知道你的密码了?至少也知道你在试图检查这个哈希。
然而他们并不能知道。非常巧妙地,这个功能使用了一个叫 k-anonimity 的特性,让数据提供方无法知道用户在查询什么密码,但是用户又能知道自己的密码是否在数据提供方的数据库里。
举例来说,如果你的密码是 hunter2
,则 1Password 在浏览器本地计算其 SHA-1 哈希值为 F3BBBD66A63D4BF1747940578EC3D0103530E21D
。取其前 5 位 F3BBB
,构造一个 GET 请求 https://api.pwnedpasswords.com/range/F3BBB
这个地址。返回的结果中包含了所有以 F3BBB
开头的密码的 SHA-1 哈希值(省略前五位),以及其被泄露的次数。现在这个接口会返回 528 行数据,其中有一行是 D66A63D4BF1747940578EC3D0103530E21D:17043
。于是,我们就知道 hunter2
这个密码被泄露过 17043 次(由 HIBP 数据库统计)。由始至终,无论是数据提供方 HIBP 还是任何可能的线路窃听者,唯一知道的信息就是「有人查询了 SHA-1 值为 F3BBB
开头的一个密码」,但是这个密码是啥、这个密码的完整哈希值是啥、到底在不在数据库里等信息,都是无从得知的。