哪种密码存储方法更安全

时间:2014-04-05 04:27:15

标签: encryption hash passwords

哪种更安全的密码存储方法?我缺乏确定答案的数学背景。

让我们为了论证而假设为以下每种方法生成的所有密码和用户名是随机生成的6个字符,已知正好是6个字母 - 特殊字符字段,并且每个字符都使用相同的散列算法和相同数量的通行证。

  1. 标准方式。 UserName以纯文本格式存储,只发现密码。哈希(PlaintextPassword + UniqueRecordSalt)=存储在DB中的密码。

  2. 一个字段被识别为LoginInfo = Hash(加密(用户名,密码)+共享盐)。用户名和密码都不会以任何其他格式存储。

  3. 强制交叉尝试用户名/密码组合是否抵消了共享盐的弱点而不是唯一的记录盐?这当然完全忽略了对可用性的所有影响,并完全专注于安全性。

    任何人都可以指点我帮助我自己回答这个问题,因为我缺乏加密和数学知识来自己得出答案吗?

    请随意将其移至更合适的论坛。我不知道还能把它放在哪里。但是,我觉得这不是一个与程序员整体日常工作无关的话题。

2 个答案:

答案 0 :(得分:1)

正如您所说,选项1是存储密码的标准方法。只要您使用具有唯一salt的安全散列函数(例如NIST recommend PBKDF2),您的密码就是安全的。所以我建议这个选项。

选项2并没有多大意义。你不能撤消'哈希函数,为什么要加密其内容?然后,您还必须将加密密钥存储在完全不同的问题。

另外,共享盐是什么意思?如果你总是使用相同的盐,那么就会破坏你的哈希值。每排独特的盐是可行的方法。

我想说将用户名和密码组合到一个哈希中会使事情过于复杂,并限制您在开发中的选项,因为您无法从给定用户名的数据库中获取一行。

假设您想在5次错误密码尝试后锁定用户。使用标准的纯文本用户名和散列的pw,您可以拥有一个' login_attempt_count'列并在每次错误输入密码时更新该用户的行。

如果您的用户名和密码被一起散列,则无法通过登录尝试计数来识别要更新的行,因为具有错误密码的散列正确用户名将不匹配任何散列。 我猜你可以使用某种映射函数来获取一个给定用户名的row_id,但我会说它只是不必要的复杂,并且复杂性更大,你有更大的安全漏洞的可能性。

正如我所说,我只选择选项1.它是存储密码的行业标准方式,对于几乎任何应用程序来说都足够安全(只要您使用现代安全散列函数)。

答案 1 :(得分:1)

请先阅读How to securely hash passwords?。总结一下:

  • 永远不要使用任何哈希算法的单遍传递。
  • 永远不要自己滚动,这就是你的例2;(例如1,如果+表示连接)。
  • 用户名存储在明文
  • 每个用户生成的盐,8-16个随机字节,以明文形式存储
    • 纯二进制或编码为Base64或十六进制或任何你喜欢的。
  • 使用BCrypt,SCrypt或PBKDF2
  • 使用高达工作因素/成本/迭代次数,因为您的CPU可以在预期的未来高峰时间内处理。
  • 特别是对于PBKDF2,不要求比本机哈希产生更多的二进制输出字节。不过,我会说不少于20个二进制字节。
    • SHA-1:输出= 20个字节(40个十六进制数字)
    • SHA-224:20个字节< =输出< = 28个字节(56个十六进制数字)
    • SHA-256:20个字节< =输出< = 32个字节(64个十六进制数字)
    • SHA-384:20个字节< =输出< = 48个字节(96个十六进制数字)
    • SHA-512:20个字节< =输出< = 64个字节(128个十六进制数字)
  • 特别是对于PBKDF2,SHA-384和SHA-512目前在64位系统上具有比较优势,因为2014年老式GPU的许多攻击者将使用64位操作的优势比你的防御更小CPU比在32位操作上要好。

如果你想要一个例子,那么可以根据PHP source code查看PHP.net Password Hashing FAQ,特别是password_hash()和password_verify()函数。

或者,我在my github repositories处有各种(目前非常粗糙的)密码哈希示例。现在它几乎完全是PBKDF2,但我将来会添加BCrypt,SCrypt等等。