是否有使用 PBKDF2 作为密码哈希的标准?

Posted

技术标签:

【中文标题】是否有使用 PBKDF2 作为密码哈希的标准?【英文标题】:Is there a standard for using PBKDF2 as a password hash? 【发布时间】:2010-12-01 04:39:20 【问题描述】:

和我一起对抗弱密码哈希。

PBKDF2 密码散列应该包含盐、迭代次数和散列本身,以便以后验证。 PBKDF2 密码哈希是否有标准格式,例如 RFC2307 的 SSHA? BCRYPT 很棒,但 PBKDF2 更容易实现。

显然,没有规格。所以这是我的规格。

>>> from base64 import urlsafe_b64encode
>>> password = u"hashy the \NSNOWMAN"
>>> salt = urlsafe_b64decode('s8MHhEQ78sM=')
>>> encoded = pbkdf2_hash(password, salt=salt)
>>> encoded
'PBKDF21000$s8MHhEQ78sM=$hcKhCiW13OVhmLrbagdY-RwJvkA='

更新:http://www.dlitz.net/software/python-pbkdf2/ 定义了一个 crypt() 替换。我更新了我的小规格以匹配他的,除了他以$p5k2$ 开头而不是PBKDF2。 (我需要从其他 LDAP 样式的 SCHEMES 迁移出去)。

这是PBKDF2,小写十六进制的迭代次数,$urlsafe_base64 编码的盐,$,以及urlsafe_base64 编码的 PBKDF2 输出。 salt 应该是 64 位,迭代次数至少应该是 1000 次,带有 HMAC-SHA1 输出的 PBKDF2 可以是任意长度。在我的实现中,默认情况下它总是 20 个字节(SHA-1 哈希的长度)。

密码在通过 PBKDF2 发送之前必须编码为 utf-8。没有关于它是否应该被规范化为 Unicode 的 NFC 的消息。

这个方案的暴力破解成本应该比 SSHA 高出 iterations 倍。

【问题讨论】:

在我写完这篇文章后的一段时间,我意识到 RedHat Linux 中使用了基于 SHA 的 crypt 替换。它可能比所讨论的 PBKDF2 方案设计得更好。 en.wikipedia.org/wiki/Crypt_%28Unix%29#SHA-based_scheme 【参考方案1】:

PBKDF2 的参数(盐和迭代)有一个规范,但它不包括哈希。这包含在PKCS #5 version 2.0 中(参见附录 A.2)。一些平台内置了对这种 ASN.1 结构的编码和解码的支持。

由于 PBKDF2 确实是一个密钥派生函数,因此指定一种将“哈希”(实际上是派生密钥)与派生参数捆绑在一起的方法是没有意义的——在正常使用中,密钥必须保密,并且永远不会被存储。

但是对于用作单向密码哈希,哈希可以存储在带有参数的记录中,但在它自己的字段中。

【讨论】:

【参考方案2】:

我会和你一起对抗弱哈希。

OWASP 有一个密码存储备忘单 (https://www.owasp.org/index.php/Password_Storage_Cheat_Sheet) 以及一些指导;他们建议截至 2012 年至少进行 64,000 次 PBKDF2 迭代,每两年翻一番(即 2012 年为 90,510)。

请注意,为每个用户 ID 存储一个长的、加密的随机盐始终是基本的。

请注意,每个用户 ID 的迭代次数变化很大,并将迭代次数与 salt 一起存储会增加破解软件的复杂性,并可能有助于排除某些优化。例如,“bob”使用 135817 次迭代进行加密,而“alice”使用 95,121 次迭代,即 2013 年可能至少为 (90510 + RAND(90510))。

还要注意,如果允许用户选择“password”、“Password1!”、“P@$$w0rd”和“P@$$w0rd123”等弱密码,所有这些都是无用的,所有这些都将确实很快就可以通过基于规则的字典攻击找到(后者只是具有以下规则的“密码”:大写首字母,1337-speak,在末尾添加一个三位数)。拿一个基本的字典列表(phpbb,一个好的,小的初学者词表)并应用这样的规则,你会破解很多密码,人们会尝试“聪明”的技巧。

因此,在检查新密码时,不要只应用“所有四个大、小、数字、数字,至少 11 个字符长”,因为“P@$$w0rd123”符合这个看似非常严格的规则。相反,使用该基本字典列表并查看基本规则是否会破解它(这比实际尝试破解要简单得多 - 您可以将您的列表及其单词小写,然后简单地编写类似“如果最后 4 个字符是一个共同的年份,检查除了最后四个字符之外的所有字符”,以及“如果最后 3 个字符是数字,则检查除最后​​ 3 个字符之外的所有字符”和“检查除最后​​两个字符之外的所有字符”和“De-1337 密码 - 将 @ 转换为 a,将 3 转换为 e,依此类推,然后对照单词表检查它并尝试其他规则。”

就密码短语而言,一般来说是个好主意,特别是如果在单词中间添加了一些其他字符,但当且仅当它们足够长时,因为你放弃了很多可能组合。

请注意,即使在 2012 年,配备 GPU 的现代机器每秒也有数百亿次哈希迭代(MD5、SHA1、SHA-256、SHA-512 等)。就单词组合“正确的马”而言电池主食”类型的密码,这个充其量是一个非常普通的密码 - 它只有 4 个长度为 7 或更少的全小写英文单词,带有空格。因此,如果我们去寻找具有 180 亿次猜测的 XKCD 风格密码: 一个现代小型美国英语词典有: 6k 个长度为 5 或更少的词 21k 个长度为 7 或更少的词 36k 个长度为 9 或更少的词 46k 个词长度为 11 或更少的 49k 个长度为 13 或更少的单词

使用 XKCD 风格的密码短语,并且无需费心按受欢迎程度过滤单词(“正确”、“椅子”、“笨蛋”和“出血”),我们有 21k^4,这只有大约 2E17 种可能性。使用 180 亿/秒的设置(如果我们面临单次 SHA1 迭代,一台机器有 8 个 GPU),大约需要 4 个月来彻底搜索键空间。如果我们有十个这样的设置,那就是大约两周。如果我们排除像“dumpier”这样不太可能的词,那么快速第一次通过会快很多。

现在,如果您从“巨大”的 linux 美式英语单词表中找到单词,例如“Balsamina”或“Calvinistically”(均使用“转到行”功能选择),那么我们将有 30k 个单词长度 5 或更少 115k 长度 7 或更少 231k 长度 9 或更少 317k 长度 11 或更少 362k 长度 13 或更少

即使有 7 个最大长度限制,以这个庞大的字典为基础并随机选择单词,我们有 115k^4 ~= 1.8E20 种可能性,如果设置保持最新,则大约需要 12 年(功率翻倍每 18 个月)。这与 13 个字符、小写字母 + 仅限数字的密码极为相似。大多数估计会告诉您“300 年”,但他们没有考虑摩尔定律。

【讨论】:

那个备忘单看起来很有用。你的帖子在句子中间结束,你能解决它吗?另外,您如何看待 passPHRASES 而不是 passWORDS?见xkcd.com/936 我刚刚将@Anti-weakpasswords 的两个半答案合并为一个答案。我没有做任何更改,只是复制了另一个帖子并粘贴到这里。

以上是关于是否有使用 PBKDF2 作为密码哈希的标准?的主要内容,如果未能解决你的问题,请参考以下文章

使用Pbkdf2加密加密和验证使用Salt的哈希密码

SHA1 在 PBKDF2 中用作散列函数是不是仍然安全?

ruby 在Ruby中安全实施salted PBKDF2密码哈希(请参阅https://crackstation.net/hashing-security.htm)

Java 中带有 bouncycastle 的 PBKDF2

密码安全存储——PBKDF2bcryptscrypt

Java - 使用 HMACSHA256 作为 PRF 的 PBKDF2