为什么PHP的crypt()函数在哈希值前添加盐值?

11

我正在研究如何构建一个登录系统,阅读了php手册后得知如果你向crypt()函数传递一个两位数的盐值,它会返回一个hash字符串,而这个字符串前两位就是你使用的盐值。

示例:

$salt = "kr";
echo crypt("mysecret",$salt); //returns "kreOI.F7eOQMY"

我的第一个想法是,这不会帮助那些试图反向破解你的哈希值的人吗?

我在维基百科上查了一下关于的内容:

为了最好的安全性,盐值应该保密。

那么我不明白为什么crypt函数返回的所有哈希值都以使用的盐值作为前缀呢?

这是否有原因?这是否应该是一个安全问题?


这就是为什么我推荐使用两部分盐的原因。一部分保存在配置文件中,它是保密的,但对于所有用户都相同;另一部分与密码一起保存,它对于每个用户都不同/随机生成。 - CodesInChaos
@meager,实际上我正在尝试升级一个我继承的项目。 - JD Isaacks
3
维基百科不应被视为真理。盐并非秘密。 - President James K. Polk
6个回答

31
维基百科文章的作者混淆了盐和搜索空间的概念,暗示盐是一种防止暴力破解攻击的方法。混淆这些概念并不能提高安全性;一个不能识别和区分这两个问题的人不是可信的指导者。
盐的目的是防止预先计算好的查询表(如彩虹表)。盐可以阻止攻击者用“时间”换取“空间”。每一位盐都会使存储表的要求加倍;两个字节的盐会有很大的影响(65536倍),但八个字节需要不存在的“尧比特”存储设备来进行查询表。
假设盐不能保密,则鼓励更好的密钥加强和密码选择,从而导致更安全的系统。
然而,最近NIST的建议鼓励使用额外的秘密“盐”(我见过其他人把这个额外的秘密称为“胡椒粉”)。可以使用此秘密作为盐执行一次密钥派生的附加迭代。与增强针对预计算的查找攻击的强度不同,此轮保护针对实时字典攻击。因此,它更像是一个良好密钥派生函数中的大量迭代。
如果将此密钥存储在散列密码中,则此密钥无用;必须将其作为秘密进行管理,这可能在大型用户数据库中很困难。
暴力攻击最好通过密钥加强(对哈希函数进行数千次应用)和密码选择规则(要求更长的密码,拒绝黑名单条目等)来防止,但“胡椒粉”提供了一个额外的防御层。

在这里查看相关答案:https://dev59.com/_HVC5IYBdhLWcg3wsTRi#215165 - erickson

9

2
是的,盐应该是保密的,但是密码哈希也一样。将它们同样保密放在一个地方是完全可以接受的。要检查密码是否与哈希值相符,必须将盐与密码组合并与哈希值进行比较。因此,任何有权查看密码哈希值的用户或进程也应该有权查看盐,因为仅有密码哈希值本身无法用于检查密码(除非您想破解盐)。
盐的目的是,如果两个不同的用户拥有相同的密码,则它们将生成不同的哈希值。这也意味着字典攻击更加复杂,因为您不能只对所有可能的密码进行哈希,然后将它们与用户密码哈希列表进行比较以找到多个用户密码。相反,您需要尝试为单个盐尝试密码以找到一个用户的密码,或者尝试使用多个盐在所有可能的密码组合中寻找匹配项。但是,仅凭盐的知识并不能逆转密码哈希值。这只意味着您可以对密码哈希值进行字典攻击。
如果您可以找到一种方法来使盐比哈希值更安全,那当然是好事,但是很难看出当需要访问其中一个程序时,如何实现这一点。

1

crypt()函数已经过时。它用于为旧式Unix系统的密码进行哈希处理,在影子密码支持出现之前使用。盐值存在是为了增加暴力破解密码的复杂度。然而,由于盐值是由密码子系统随机生成的,因此必须以明文形式存储,以便将来的密码操作能够正常工作。如果在加密之前将盐值嵌入到密码中,那么就没有实际的方法来验证密码 - 每次进行密码检查时都必须尝试每个可能的盐值 - 这是非常不切实际的。因此,盐值被添加到加密后的密码前面,这样您就可以再次提取它以供将来使用。

crypted password: xxabcdefghijklmn
                  ^^- salt
                    ^^^^^^^^^^^^^^-- crypted pw

if ('xx' + crypt('xx' + password) == 'crypted string') then
     password is ok
endif

现在,crypt()函数的安全性相当于谷类食品盒子解码器。它只是为了历史目的而存在,并且用于低安全性的“不关心是否被破解”的存储。对于任何现代密码使用,您最好使用更现代的哈希函数,例如sha1 / sha256 / md5。即使md5在今天也被认为是不安全的,sha1也有一些漏洞,而(据我所知)sha256仍然是安全的。

这很有道理,所以如果我已经有了一个数据库,里面存储了哈希密码的用户。我能否将它们转换为其他类型,如Sha256,还是应该在下次用户登录时“转换”它们?显然那个时候我会有原始密码。谢谢。 - JD Isaacks
6
盐值的作用不是为了增加暴力破解的复杂度,而是为了防止彩虹表的时间-空间权衡攻击。 - erickson
19
crypt()函数并非“过时”- 当然,基于DES的较旧哈希在现在有些可疑,但MD5、Blowfish和(在较新的PHP中)基于SHA的哈希完全适用于现代密码哈希处理。 - TML
1
@John - 我处理转换问题的方式只是对哈希进行哈希。这会增加密码验证过程中的一个步骤,但可以忽略不计。我读过一些数学文章,表明这样做可能会增加碰撞的几率,但并不显著,即使发生碰撞,在身份验证机制中也不是我的真正担忧。@TML - 为什么要捍卫crypt()?每个人都可以继续使用bcrypt,我们都会因此变得更好。 - Shane H
1
SHA和盐是苹果和香蕉。没有盐的SHA比任何带盐的老算法都要糟糕。因此,这并没有真正回答问题。 - deceze
显示剩余2条评论

1

盐被附加到哈希值中,这样当您获取密码并想要查看它是否与哈希值匹配时,您将知道要使用哪个盐。这里的想法是为每个密码使用不同的盐,以便某人无法预先计算哈希表。

您还可以向每个密码附加第二个盐(对所有密码相同),并且不告诉任何人它是什么。


1

PHP crypt继承了UNIX crypt()函数的行为,该函数用于在UNIX passwd文件中生成密码哈希。必须将盐存储在某个地方,否则以后无法验证密码是否正确。对于passwd文件,简单的做法是将盐(始终是两个字符)前置到加密密码的开头,这样可以将其简单地存储在单个字段中。

盐值应保密的说法容易被误解。按照最佳实践,您不应该公开您的盐值,就像您不应该公开您的密码哈希一样。将哈希和盐值提供给攻击者会使他们能够轻松进行暴力攻击,而不会在您的系统上产生可疑的流量。但是,即使攻击者可以看到盐和哈希密码,系统仍应保持安全。

事实上,您无论将哈希存储在哪里,在原则上都可能会被黑客以与哈希密码相同的方式破坏。如果密码检查代码可以访问它,则必须假定已经入侵系统的人也可以访问它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接