创建一个安全的,基于Web的密码管理系统,能够在用户之间共享数据

Mic*_*ley 6 ruby security encryption openssl public-key-encryption

我提前为传入的Wall-O-Text道歉.这是(至少对我来说)一个相当复杂的问题,我已经考虑了很多.你可以阅读我的问题,也可以在这个GitHub Gist中看到Ruby中的测试实现(非常匆忙构建,而不是数据库支持,可能非常难看).


介绍

想象一下,需要创建一个基于Web的密码管理系统(通过SSL!:),具有以下要求:

  1. 个人用户使用他们自己的唯一密码短语登录系统.
  2. 这个密码短语应该足以让用户有效地使用系统(例如从智能手机等) - 关键是他们不应该用它们保存密钥文件.
  3. 用户可以在系统中存储任意长度的数据位("条目").
  4. 条目在数据库中加密,使得数据库或应用程序中没有足够的信息来读取加密的条目.
  5. 用户应该能够与系统的其他用户"共享"条目,以便其他用户可以读取条目的内容.

我不是密码学方面的专家.在考虑了一段时间后,我想出了以下内容. 我的问题是:这种实施安全吗?我错过了什么吗?如果是这样,上述规范是否可以实现?或者这有点矫枉过正?

数据库

数据库设置如下:

+------------------------------------------------------------------------------+
|  users                                                                       |
+---------+--------------+--------------+---------------+----------------------+
| salt    | pub_key      | enc_priv_key | priv_key_hmac |                      |
+---------+--------------+--------------+---------------+----------------------+
|  entries                                                                     |
+---------+--------------+--------------+---------------+----------+-----------+
| user_id | parent_entry | enc_sym_key  | sym_key_sig   | enc_data | data_hmac |
+---------+--------------+--------------+---------------+----------+-----------+
Run Code Online (Sandbox Code Playgroud)

基本用例

让我们想象一下系统的两个用户,Alice和Bob.

鲍勃报名参加该网站:

  • 鲍勃输入密码.此密码将发送到服务器(但不存储).
  • 服务器生成随机盐并将其存储在salt字段中.
  • 服务器生成Bob的密码和salt的SHA-256哈希值.
  • 服务器生成RSA密钥对.公钥在pub_key字段中以纯文本格式存储.私钥通过AES-256使用Bob的密码和salt生成的哈希作为密钥加密并存储在enc_priv_key字段中.
  • 服务器使用Bob的密码和salt作为密钥为Bob的私钥生成基于哈希的消息认证代码,并将其存储在priv_key_hmac字段中.

Bob 在系统中存储一个条目:

  • Bob输入一些数据作为条目和密码存储.此数据将发送到服务器.
  • 服务器生成一个密钥,用作AES-256加密的密钥.
  • 服务器使用此密钥加密数据并将结果存储在enc_data字段中.
  • 服务器使用生成的密钥为数据生成基于散列的消息验证代码,并将其存储在data_hmac字段中.
  • 用于加密数据的对称密钥使用Bob的公钥加密并存储在enc_sym_key字段中.
  • 服务器使用Bob的私钥生成对称密钥的签名.

Bob 检索他存储的条目:

  • Bob输入他的密码和要检索的条目的ID.
  • 服务器生成Bob的密码和salt的SHA-256哈希值.
  • Bob的加密私钥使用散列通过AES-256加密解密.
  • 服务器通过检查HMAC来验证Bob的加密私钥是否未被篡改priv_key_hmac.
  • 服务器enc_sym_key使用Bob的私钥解密存储在字段中的对称密钥.
  • 服务器通过sym_key_sign使用Bob的公钥验证签名来验证加密的对称密钥未被篡改.
  • 服务器使用对称密钥解密数据.
  • 服务器通过验证存储在data_hmac字段中的HMAC来验证加密数据未被篡改.
  • 服务器将解密的数据返回给Bob.

Bob 和Alice分享了一个条目:

  • Bob希望Alice能够访问他拥有的条目.他输入了他的密码和要分享的条目的ID.
  • 使用"Bob检索其存储的条目"中的方法解密条目的数据.
  • 以与"Bob在系统中存储条目"相同的方式为Alice创建新条目,但以下情况除外:
    1. 该条目parent_entry设置为Bob的条目.
    2. 使用Bob的私钥计算对称密钥的签名(因为Bob的私钥不可用于Bob).
    3. 当Alice访问此新条目时,非null的存在 parent_entry会导致系统使用Bob的公钥来验证签名(因为他的私钥用于创建它).

Bob 更改了共享条目中的数据:

  • Bob决定更改他与Alice共享的条目中的数据.Bob指示要修改的条目ID以及它应包含的新数据.
  • 系统将覆盖"Bob在系统中存储条目"中创建的数据.
  • 系统查找每个条目parent_entry等于刚修改的条目,并且每个条目都覆盖"Bob与Alice共享条目"中创建的数据.

分析

好处:

  • 如果没有拥有数据的用户的密码,就无法解密数据库中的任何数据,因为解密数据所需的私钥是用用户的密码加密的,并且密码(和它的哈希)不存储在数据库.
  • 如果用户想要更改其密码,则只需要重新生成其加密的私钥(使用旧密码/散列解密私钥,然后使用新密码/散列重新加密).
  • 共享条目作为实际的单独记录存储在数据库中,因此无需在多个用户/用户组之间共享密钥.

缺点/问题(我能想到):

  • 如果修改了共享条目,则系统必须重新加密每个子条目; 由于大量用户共享数据,这可能在计算上很昂贵.
  • 共享条目取决于父用户的公钥用于签名验证.如果删除用户或其密钥更改,则签名无效.

从介绍中反复出现:我的问题是:这种实施安全吗?我错过了什么吗?如果是这样,上述规范是否可以实现?或者这有点矫枉过正?

谢谢你坚持这么久.我对你的意见感兴趣!我是在正确的轨道上,还是一个完整的白痴?你决定!:)

sar*_*old 1

没有静脉注射储存?我猜你可以使用 AES-256-ECB,但这只允许用户存储 32 字节密码,并且你需要确保生成的私钥仅用于一种加密。(您当前的设计在这方面似乎是安全的,但如果您希望允许密码长度超过 32 个字节,或者考虑让此密钥发挥双重作用,则需要为每次加密存储 IV。)

我看不到priv_key_hmac和的安全价值data_hmac;如果私钥或加密数据被篡改,则使用私钥或对称密钥解密将产生垃圾输出。当鲍勃不知道如何输入该BEL字符时,他肯定会产生怀疑。:)(人类会看到输出吗?人类可能会意识到返回的密码是不正确的,而无需告知。计算机无法区分其中的差异,因此,如果自动化系统将使用生成的密码,那么当然,请保留田野。)

没有“我忘记了密码”的机制。确保您的用户知道,如果忘记密码,他们的数据将无法恢复。如今,用户受到了宠爱,并且可能也期望受到您的服务的宠爱。

我没有看到用户可以指定鲍勃想要解密哪个条目的机制。您应该为每个条目存储一个名称,或者像ssh(1)中那样known_hosts,存储名称的散列版本。直接存储名称将消除 SHA-256 操作,但报告用户拥有帐户的服务的明文名称的数据库泄露可能同样具有破坏性。(也许是在线陪护服务,或离岸银行,或搏击俱乐部。)