如何在客户端浏览器中存储 RSA 私钥以获得更好的用户体验?

How to store RSA private keys in the client's browser for better UX?

我打算编写一个基于 Web 的密码管理软件,以便在团队组和成员中提供共享和权限功能。

编辑:

正如 Luke Park 在下面提到的,我想补充一点,我正在使用带有令牌(过期)的用户密码授权。所有授权 API 调用都提供令牌,使这些调用只能由注册用户访问。是的,应用程序由 SSL 包装,使服务器和客户端之间的通信更加安全。

编辑结束

目前我已经做了很多关于找到处理密码加密的正确模式的研究。我正在查看的模式称为混合加密,因为它适用于多个客户端并且可以安全地实施。以下是我将如何在我的应用程序逻辑中实现此模式:

客户想要创建密码

  1. Andre 创建密码并以纯文本形式提供密码
  2. 前端应用程序生成一个称为会话密钥的对称密钥
  3. 客户端使用会话密钥加密明文密码
  4. Andre 选择将密码分享给 Bob
  5. 客户端通过 REST
  6. 从服务器应用程序检索 Bob 的 public 密钥
  7. 客户端用 Bob 的public密钥
  8. 加密会话密钥
  9. 客户端将加密的会话密钥和加密的密码传输给服务器

服务器应用程序处理密码提交

  1. 服务器应用程序使用只有服务器应用程序知道的私有盐对数据进行加密
  2. 服务器将数据存入数据库

正在检索共享密码

  1. Bob 请求 Andre 最近共享的密码
  2. 服务器用私有salt解密请求的数据集并发送给客户端
  3. 客户端使用Bob的私钥(由Bob的电脑提供)解密会话密钥
  4. 使用解密的会话密钥,客户端现在可以解密密码(例如现在可以复制)

到目前为止,我认为这种模式足够安全,因为客户端的所有私钥都不是 public 并且仅对客户端可见。为了在基于 Web 的应用程序中实现这一点,我发现 openpgpjs.org 可以在客户端生成 public 和私钥,并使用这些密钥加密或解密数据。最重要的是,私钥字符串可以通过秘密密码保护。

我的问题是,如何在不影响用户体验的情况下将私钥文件实施到我的前端应用程序中?我不想强制用户手动管理他的私钥并强制他在每次密码请求时提供密钥。将私钥文件存储到浏览器的本地存储并在每次密码请求时从本地存储获取私钥是否安全?

您仍然无法验证您的客户端和服务器。因为任何人都可以 public 只请求 public 密钥,所以很容易在客户端和服务器之间进行 MITM,并向其提供垃圾任意密码。

考虑:

  • 攻击者使用 REST 检索 Bob 的 Public 密钥。
  • 攻击者生成他们自己的 "Session Key" 并加密一些任意值。
  • 攻击者将结果提供给已经 Man-in-the-middled.
  • 的客户端
  • 客户端认为它收到了正确的密码,但没有收到。

如果无法同时验证和授权服务器和客户端,您的系统将变得很容易搞砸。这可能看起来不是一个大问题,但它很容易为其他攻击让路。

编辑:还注意到,如果在客户端和 REST API 之间发生 MITM,攻击者可以向客户端提供一个 public 密钥与攻击者拥有的私钥配对。

考虑:

  • 客户端即将与服务器共享其密码。
  • 客户端联系 REST API(MITM 攻击者)以获取 Bob 的 Public 密钥。
  • 攻击者提供他们的 Public 密钥。
  • 客户端生成会话密钥和密码密文。
  • 客户端将结果发送给"server"(攻击者)。
  • 攻击者使用他们的私钥来检索会话密钥。
  • 攻击者使用会话密钥检索明文密码

经过一番思考,我决定用用户主密码加密 RSA 私钥。由于主密码无处存储,我假设将加密的私钥存储到用户的本地浏览器存储中是安全的。该解决方案可以在每次密码请求时自动提供加密的私钥,但会强制用户在每次请求时输入密码,这对我来说现在没问题。

如果有人有更好的解决方案,如果你能在这里post你的解决方案,我会很高兴。

老实说,整个 'using a session key as a symmetric encryption token' 场景对我来说似乎有点多余。它可能会增加一定程度的模糊性,但传递加密数据 + 加密令牌的整个想法对我来说似乎有点奇怪。

我也会尝试重新考虑一下设置。说到底,这是一个基于Web的应用程序,通过ACL和密码权限访问这个不是更简单吗?

正在创建密码

  1. Bob 创建明文密码
  2. 应用程序使用 Bob 的 public 密钥(登录时获得)加密密码
  3. 应用使用JWT通过REST保存加密后的密码(登录时获取)

正在找回密码

  1. Bob 试图以明文形式查看他的密码
  2. 应用程序使用 JWT 向 REST 发出请求(登录时获取),请求 Bob 的私钥
  3. 应用收到私钥,解密密码,显示出来[=44​​=]

共享密码

  1. Bob 决定与 Mark 分享他的一个密码(假设每个密码旁边都有一个名为与 MARK 分享的神奇按钮)
  2. 应用程序使用 Bob 的 public 密钥
  3. 加密 Mark 的用户令牌
  4. 应用程序使用 JWT 向 REST 发送请求(登录时获取),发送 Mark 的加密用户令牌
  5. REST 应用程序收到请求,使用 Bob 的私钥解密 Mark 的用户令牌
  6. REST 应用程序为 Bob-Mark 关系创建新的 RSA 对,其中 Bob 是提供者,Mark 是接收者
  7. REST 应用程序在 Mark 的用户令牌下创建新密码,并使用这个新的 RSA 对对其进行加密

TL;TR

请勿共享密码本身。相反,花一些时间研究 ACL 并为用户对(提供者 + 接收者)生成新的 RSA 对。下一步是为共享密码创建特殊的 table - 它会更干净,并且在处理撤销权限时也会让人头疼。