小黑盒游戏新闻 ( ) • 2024-04-30 14:56

存在浏览器里的密码使用起来是非常方便的,大家应该也都习惯了密码自动填写的便利,但是这些密码是如何存储的,以及浏览器批量存储这些密码真的安全吗,这都是需要进一步考量的,正好之前了解过主流的两类浏览器(及使用这两类浏览器内核)的密码管理协议,在这里做一下分享

本篇文章可能有点硬核,我尽量把它写的更偏科普一些。为了不引起歧义,下文中我们把用户存储的网站和账户密码称作"口令"。

1.firfox记住密码的原理

Firefox利用了本地客户端、密码服务器和一个安全密码查询协议和一个密码验证协议来实现整个功能。

简单而言,客户端存储账户信息和密码(该密码不会发送给密码服务器,否则会使服务器得到所存储密钥加密值的明文),客户端利用密码验证协议证明自己为密码持有者,为自己产生会话令牌,服务器会验证该令牌。服务器会将口令分为A类和B类:

  • A类:即使用户忘记了密码,也可以通过证明对电子邮件地址的控制权(ID)并重置帐户来恢复分配给此类的口令。A类主密钥存储在服务器上,用于帮助用户找回丢失口令,不同数据类型的单个加密密钥均派生自 kA。
  • B类:如果忘记密码,则无法恢复此类中的数据。即使拥有用户的IdP 无法读取它。密钥服务器只存储 wrap(kB),并且永远不会看到 kB 本身。客户端(浏览器)使用从用户密码派生的密钥(这一步使用了 Key Stretching,得到B类密钥的派生密钥)来解密 wrap(kB),获取真正的 kB。

下面将会非常详细地对整个流程进行分析,如果确实看不懂可以调到第二部分chrome的密码记录分析

1.1 创建口令管理账户并分配秘钥

当用户在客户端输入email和password后,运行100轮派生函数PBKDF2,使用用户的邮件地址作为salt生成quickStretchedPW,然后将其作为HKDF的参数,得到authPW,在此过程中得到了密码强度足够(主要是保证随机性)的派生密钥authPW。最后,将邮件地址和对应的authPW发送给服务器。

服务器得到密钥后,会对该密钥进一步扩展,同时生成哈希表存储索引值便于遍历。

1.2 口令查询/回话建立

口令查询包括获得会话令牌和创立会话

我们先来谈谈获得会话令牌,主要步骤如下图所示:

要将客户端连接到服务器,需要使用登录协议将电子邮件+密码对转换为sessionToken。

该协议首先在客户端将密码和电子邮件地址输入 1000 轮 PBKDF2 以获得quickStretchedPW,将 quickStretchedPW 输入 HKDF 以获得authPW,然后将电子邮件 + authPW 发送到服务器的端点。

服务器使用电子邮件地址查找数据库行,提取authSalt,执行与帐户创建期间相同的拉伸,以获取bigStretchedPW,验证哈希值得到verifyHash,然后将verifyHash与存储的值进行比较。如果它们匹配,则客户端已证明知道密码,服务器将创建一个新会话。服务器将新生成的会话令牌及其帐户标识符 (uid) 返回给客户端。

值得注意的是,在本协议中,服务器支持每个帐户同时存在多个会话(通常每个客户端设备一个会话,可能还有其他用于帐户管理门户的会话)。这代表sessionToken 永久存在(直到被密码更改或显式吊销命令撤销),并且可以无限次使用。

对于已验证电子邮件地址的帐户(具有会话令牌sessionToken)的客户端可以使用api获取经由签名的浏览器 ID/用户配置证书。然后,可以使用此证书生成已签名的浏览器 ID assertions,传递到服务器的./certificate/sign。过程如下图所示:

1.3 更改/找回密码

当用户更改其密码(即他们仍然知道旧密码)时,或当用户忘记密码时,需要重置账户和密码。下面将分别讨论两者情况:

当需要利用旧密码重置密码时,需要完成下图流程:

其主要协议流程为验证用户身份,之后用户使用旧密码将存储在服务器的wrap(kb)解密,然后使用从新密码派生的新密码重新包装kb生成新的wrap(kb)。

对于找回密码,用户首先需要访问fxa-content-server(即服务器)上的页面,服务器向用户发送一封带有恢复代码的电子邮件,然后在用户单击该电子邮件中的链接,利用存储的邮件地址来验证自己的身份,从而重置密码。

具体而言,当用户申请重置密码,服务器将相应的帐户标记为“待恢复”,为该帐户分配一个随机密码ForgotToken,创建一个恢复代码,并向用户发送电子邮件,其中包含URL(指向fxa-content-server)、passwordForgotToken和恢复代码作为query-args。

当用户单击链接时,加载的 fxa 内容服务器页面会将令牌和代码提交到 API。当服务器看到匹配的令牌和代码时,它会分配一个backtoken并将其返回到提交它们的客户端(页面)。然后,客户端可以创建新的密码并重复创建账户时的密码创建流程,如下图所示:

值得注意的是,一旦重置密码,将会丢失所有B类数据,这避免盗取邮箱的攻击者获得用户的所有口令,用户只能得到存储的A类数据。

这个设置是必要的,如果B类和A类不分开存储,那么同时也会有攻击服务器的攻击者,一旦得到服务器控制权,就可以解密A类,但是无法解密B类口令。

1.4 安全性分析

本协议使用基于 HKDF 的派生密码用于保护本协议中请求的内容。HKDF 用于创建多个与消息长度相等的随机字节,然后将这些字节与明文进行 XOR 运算以生成密文。然后从密文计算 HMAC,以保护消息的完整性。之所以使用该方案是因为HKDF首先可证明计算安全,其次易于指定且易于实现(只需要实现HMAC和异或操作)。

那么盗号🐶该如何盗号呢?首先,攻击者可以暴力枚举kb,但这意味着他们必须对要测试的每个猜测的密码进行1000轮PBKDF和其他操作,这是计算上困难的,用自己家的电脑得算上几万年

其次,盗号🐶可以通过web漏洞如果控制了服务器(如利用中国蚁剑等webshell工具获得服务器webshell),获得了文件读写权限,那么攻击者也无法解密wrap(kB)。此时A类数据将会被窃取。

第三,即使盗号🐶利用盗取的邮箱重置用户密码,也无法访问B类数据(关于ab类,在文章开头我们定义了,它代表数据的安全级别),B类数据会丢失。此时A类数据将会被窃取。


2.Chrome浏览器的记住密码

Chrome密码管理器采取了与firfox完全不同的安全策略,其将密码和加密过的数据均存储在用户本地,以windows为例,Chrome通常将使用windows API CryptProtectData 直接将加密后的口令存储在AppData\Local\Google\Chrome\User Data\Default\ Login Data,将密码存储在AppData\Local\Google\Chrome\User Data\ Local State。

Chromee的立场是主密码提供了一种虚假的安全感,保护敏感数据的最可行保护方式是要取决于系统的整体安全性。所有,为了执行加密(在Windows操作系统上),Chrome使用了Windows提供的CryptProtectData API,该API允许已经用于加密的Windows用户账户去解密已加密的口令。所以也可以理解为主密码就是Windows账户密码,即只要登录了Windows账号,Chrome就可以解密口令,并导入密码管理器中。

就是说,如果攻击者通过脚本(对了,就是我之前那篇文章提到的伪入库的脚本)获得了本地电脑的账号口令,那么你所有的密码都会被带走。换言之,chrome不需要像firfox那样对你的秘钥安全负责,你对自己的秘钥直接负责。

我自己写了个脚本测视了一下我的chrome,果然直接跑出了密码,确实有点离谱:

所以最后结论就是大家还是要谨慎对待记住密码这个功能,特别是使用chrome浏览器同时还经常伪入库是盒友,不然就只能: