以可逆形式存储密码 - 一个真正的用例

信息安全 加密 密码 密码学
2021-09-05 14:24:55

你怎么了,你这个疯狂的傻瓜,你不应该能够以纯文本检索密码!

我知道。听我说。

假设我有一项类似于 Mint.com 的服务。对于那些不熟悉 Mint 的人,它的工作原理如下:

  1. 用户注册 Mint.com 就像他们注册任何其他在线服务一样 - 通过提供电子邮件地址和密码。
  2. 这就是事情变得有趣的地方。然后用户从下拉列表中选择他们的银行名称,并将他们的网上银行用户名和密码提供给 Mint。
  3. 这就是事情变得非常有趣的地方。Mint 存储这些凭据并使用它们在一天中的不同时间自动检索用户进行的信用卡/借记卡交易列表。他们通过自动登录在线银行网站来做到这一点 - 使用用户凭据(大概是通过各种浏览器模拟器)。

现在我需要一种安全的方式来保护我的客户的网上银行凭证。

这是一个大问题:你会怎么做?


是的,我已经阅读了这个问题这个问题。答案都建议使用与第 3 方通信的更安全的方法(使用 API 密钥进行身份验证,或传递散列参数)。在我看来,这只是 - 一种观点 - 在我看来,这就像一个真正的用例,我有非常敏感的信息需要以纯文本形式提供 - 至少在一天中定期提供。

有趣的事实: Mint.com 拥有超过 1000 万用户

3个回答

在您描述的情况下,您代表用户存储信息,并且您没有使用它来验证用户。因此,虽然您存储的内容包括密码(几乎完全是),但您并不是传统意义上的真正“存储密码”。您正在存储秘密

相应地调整你的策略。

这两个问题都解决了;你只需要应用正确的解决方案。当“存储密码”(即验证用户)时,您会经历您熟悉的散列、盐、密钥拉伸等。但是存储秘密呢。

首先,如果可能的话,你会避免这个问题这就是存在 API 访问令牌等概念的原因。你不需要我的 Facebook 密码,因为你不能使用它。你需要一个访问令牌,Facebook 愿意在我的许可下给你。

下一个最佳解决方案是将访问权限与用户登录绑定信息使用从您用于登录的密码派生的密钥加密 - 我不知道也不存储。因此,除非您输入密码,否则我(服务器所有者)无法访问您的数据。

这很受欢迎,因为它很强大。事实上,Windows 已经这样做了很长时间,这就是为什么更改密码会导致加密文件无法访问的原因。这也是为什么您的 Windows 密码在您登录时以纯文本形式存储在内存中的原因。我建议您避免这种实施混乱。

接下来在我们的列表中,您可以分离您的进程,以使未加密的数据永远不会在面向外部的机器上可用。如果涉及 HSM,则可获得奖励积分。这里的要点是用户将他的秘密提供给网络服务器,这些秘密使用公钥快速加密到某个秘密加密设备,该设备完全无法访问,因为它没有连接。简单的秘密会立即被遗忘,然后加密的数据会被运送到某个地方的某个冷藏库。

最终,这些秘密在加密货币的帮助下在其他地方被解密并被使用。只是,发生这种情况的地方没有互联网访问权限。或者至少没有从外到内的路径。

最后,您可以尝试上面的解决方案,但失败得很惨。我只提到这一点是因为实际上所有其他解决方案都只是上述解决方案的糟糕变体:在数据库中加密,使用应用程序密码,将密码存储在另一台服务器上,将数据存储在另一台服务器上,用 [愚蠢的想法加盐你的加密密钥这里],等等。

最后,我对此类问题的标准警告适用。我会写得非常大:

你问这个问题的事实意味着你不应该这样做

严重地。存储人民银行凭证?如果您不明白自己遇到了什么样的麻烦,如果您在互联网上寻求有关如何做到这一点的建议,如果我提到的所有解决方案都不是您最关心的作为唯一可行的选择,那么您不应该实施 this

人们相信你能做到这一点。 你不会正确地做到这一点。不是因为你没有问正确的问题,而是因为你没有经常解决这个问题,以了解你会忽略哪些隐藏的陷阱。这是困难的事情:不难,但很难不犯错误。

不要让自己不知所措,从而背叛客户的信任。

虽然我不同意这种做法,但如果我必须在我的服务器中保留非常敏感的信息,我会这样做:

  • 在同一台服务器中创建一个小型守护程序,并要求操作员在启动时输入密码/密钥。
    • 从中派生对称加密密钥和非对称密钥对。
  • 仅将这些密钥保留在 RAM 上,防止交换.
  • 当用户想要将敏感信息发送到服务器时,让他们使用公钥在浏览器本身上对其进行加密(因此只有守护程序可以读取它)。让守护进程使用对称密钥对其进行加密并返回到主网络服务器——现在谁可以存储它。
  • 当系统想要对敏感信息做某事时,将加密数据传递给守护进程。然后它应该立即解密并理想地使用它(例如,发出第三方请求 - 并且不要在任何地方记录任何内容),擦除明文并仅返回响应(即主网络服务器不会“看到”明文任何时候)。

当然,您应该尽可能使用任何互补的安全实践,但该技术的核心是只有能够访问实时机器的攻击者——能够读取另一个进程的 RAM 内容——才能以明文形式检索敏感数据。或者,如果攻击者可以在服务器中注入他们自己的代码,那么在下一次重新启动时,操作员会将密码输入到他们的守护进程中。如果他们只能向守护程序发送任意命令,他们将能够做守护程序所能做的事情(即读取从这些凭据获得的数据,但不能读取凭据本身,也不执行守护程序未编程执行的操作) .

也就是说,如果 1000 万 傻瓜不太注重安全的人与我做生意,我可能会聘请最好的安全专业人员,而不是仅仅依靠我自己的知识(就像我现在所做的那样)。可能会阻止我的系统成为最薄弱的环节——尽管它可能对攻击者具有多大的吸引力。在做雄心勃勃的事情之前记住这一点,虽然可能有用,但可能会给你带来麻烦如果 当事情出错时。

我不会,除非完全确信没有人能够破解我的数据库,否则我宁愿将这些信息存储在用户的设备上,这样我的服务就不会对任何数据泄露负责。但如果我必须这样做...

当用户创建帐户时,我会生成一个 AES-256 密钥(我们称之为 a storage_key),然后使用从用户密码派生的密钥(我们称之为 a user_key)使用一些可靠的 KDF(scrypt、bcrypt、PBKDF2 ...) 并将其encrypted_storage_key与 KDF 使用的盐、迭代和其他参数一起存储,以user_key在用户帐户下派生。

然后输入/读取数据的过程如下所示:

  1. 用户输入密码。
  2. 使用选定的 KDF 和存储的参数导出user_key.
  3. 用解密存储encrypted_storage_keyuser_key,得到原始的storage_key.
  4. 使用它storage_key来加密/解密敏感数据。
  5. 不要将storage_key周围的事物保存在内存中的时间超过您可能需要的时间,并且永远不要以这种形式存储它。

当然,对于这个方案要保持任何安全性,保护用户输入他/她的密码的“线路”是至关重要的(例如,如果我们谈论的是一个网站,那么至少是 SSL/TLS)——如果有人能够获得原始密码,整个计划就崩溃了。