为什么不能使用MD5进行密码哈希?

7
我有一个朋友是一名白帽黑客。他说md5并不是真的那么糟糕,实际上如果我们使用得当,它是非常安全的。
我相信他是正确的。据我所知,有三种方法可以破解哈希值:
1.使用彩虹表(可以通过长/随机盐进行保护)
2.碰撞(可以通过多个盐或哈希来防止-如下面的例子)
3.生成时间(如果我们为每个用户使用足够长的盐值,这就不太重要了-AFAIK)
我和我的朋友认为,Blowfish并不是真正必要的,而且它可能会有害,因为它可能会减慢密码验证过程,并且即使使用更少的攻击资源,也可以用于DDOS攻击来拆毁服务器。
因此,我想确保以下算法是否真的安全?还有,是否有真正的理由选择Blowfish哈希算法?
// return a 256 bit salt + 128 bit md5 binary hash value
function hash(password, salt=null)
{
    salt = (salt != null) ? salt : Random256BitBinaryValueGenerator();
    // What about using another user-specified parameter, like email address as salt?

    return salt + md5(salt + password) + md5(password + salt);

    // Or just use a non-cryptographic hash algorithm like crc32 to prevent collisions:
    // return salt + md5(salt + password) + crc32(salt + password);

    // Or even use two different salts:
    // return salt + md5(salt + password) + md5('C' + salt + password);
}

// check password
function check(password, hash_value)
{
    return hash(password, substring(hash_value, 0, 32)) == hash_value;
}

我知道SHA2是安全的,或者Blowfish甚至更加安全,但我想知道选择这些新算法的真正原因是什么,我的意思是,如果我们可以使md5安全,为什么要选择另一个算法?根据以下链接,两个哈希值发生意外碰撞的概率是(1/2)^128,因此md5(s + p)+ md5(p + s)碰撞的概率是(1/2)^256,即使在与随机盐相结合的情况下,对于追求安全的应用程序来说也非常安全。https://dev59.com/bXVC5IYBdhLWcg3wtzut - Masoud
另外,据我所知,由于哈希输出的固定长度,目前还没有无冲突算法,因此,如果我们尚未在sha2中发现任何冲突,这并不意味着sha2是安全的(这不是因为我说的)。那么,既然我们可以保护md5,为什么要选择sha2?(如果可能的话?) - Masoud
你需要的是bcrypt,而不是blowfish。 - CodesInChaos
@CodesInChaos,bcrypt基于blowfish。 - Masoud
@Masoud 用同样的推理,你可能会说“C”,但实际上你想说的是“C#”。Blowfish是一种分组密码,bcrypt是基于blowfish密钥设置的密码哈希。 - CodesInChaos
我说bcrypt也基于blowfish。实际上,bcrypt是什么以及它来自哪里并不是很重要,但是无论如何感谢您纠正我。我再也不会把blowfish说成bcrypt了,保证! - Masoud
3个回答

16
MD5的碰撞抗性属性已经被破解了很长时间。请注意,预像抗性和第二预像抗性尚未被攻破,但是由于有更好的算法(如SHA-2),因此最好转向这些算法,而不是依赖已经开始失去其加密属性的加密哈希。注意:在存储哈希密码时,碰撞抗性属性并不重要 - 您需要确保预像抗性属性是正确的 - 即在给定某个哈希值(和盐)的情况下,找到原始密码是计算上不可行的。正如我所提到的,由于其中一个加密属性已经被破解,我担心其他属性很快也会被攻破。
当您存储密码哈希时,应该建立一些保护措施,以防止在攻击者成功提取这些哈希的情况下检索原始密码。这很重要,因为如果攻击者仅能检索密码表,则可以使用数据直接登录到您的系统,或者登录到用户重复使用相同密码的其他系统。
存储密码时,使用bcrypt、scrypt或pbkdf2等缓慢算法非常重要。合法用户只需要在首次登录时经历延迟。攻击者将不得不为他们猜测的每个密码经历延迟——请记住这里不会使用彩虹表,因为密码已经被加盐。攻击者将根据您选择的算法和迭代计数对每个密码猜测进行哈希运算。
重要的是为您的系统调整迭代次数,以便使用正确的“强度”,而不会在用户登录系统时造成真正的麻烦。这被称为“轮数”或“迭代计数”。例如,迭代约一秒钟应该足够了。可以安全地假设攻击者可以以比您系统硬件快10倍的速度运行哈希运算。因此,这将限制攻击者每秒尝试10次猜测,而不是MD5的20亿次。
关于DoS攻击

是的,你的应用在登录之前执行的额外处理可能会成为攻击者提交非常长的密码或反复发送登录请求以消耗服务器CPU和内存资源的目标。 你的担忧是正确的

以下方法可以缓解这些类型的攻击:

请记录每次登录尝试的用户名和IP地址。如果该用户名或IP地址再次出现且登录失败达到6次,则引入响应延迟。这也有助于防范密码猜测攻击。
例如,您可以人为地延迟1秒,然后2秒,然后4秒,最多到一个合理的值(例如16秒)。
这样做的好处在于攻击者无法故意锁定另一个帐户,因为合法用户只需等待16秒。
攻击者可以使用僵尸网络和随机用户名来绕过这些检查,但是他们需要比没有此控件时更多的IP地址,并且更随意的攻击者也不会意识到响应延迟是人为的。
监视系统上的登录尝试次数。一旦超过设定的阈值速率(例如每秒10次),就引入CAPTCHA以解决登录过程。您选择的阈值速率非常取决于用户基础和系统容量。
实施两步验证。仅在验证一次性密码后进行密码哈希验证。

SilverlightFox,我认为一个慢算法并不是很安全。我有两个理由,一个在问题(DDOS攻击)中已经解释过了,另一个在@martinstoeckli的回答中进行了评论。你能否请检查一下并告诉我我是否正确? - Masoud
第二个原因是:通过使用GPU,我们可以每秒计算约85亿个哈希值。对于一个包含a-z、A-Z和0-9字符的10个字符长度的密码,即使使用此软件,我们平均需要13714小时才能破解它;而对于12个字符长度的密码,平均需要6000年才能破解单个哈希值。这不足以说明问题吗?((26+26+10)^12/(8.5*10^9)/60/60/24/365/2=6017年)难道不应该强制用户选择更强大的密码,而不是使用更复杂的哈希算法吗?这些算法是否会在不久的将来成为另一个MD5,基于摩尔定律? - Masoud
另外,感谢您提供的这些非常好的建议。我使用了它们中的所有建议,但是采用了不同的方式。我为所有登录尝试设置了一个恒定的2秒延迟,并在这2秒后检查连接状态,以查看用户是否正在等待响应或连接已断开。并且在每次IP失败登录尝试后引入了验证码。您的建议比我的方法更好。 - Masoud
1
请记住,密码强度不是一个真正的指标 - 它取决于实际使用的熵源。是的,对一个10个字符的密码进行密钥空间攻击可能需要517天,但是如果您知道用户没有选择一个真正随机的密码,因为人类不是随机的,那么实际上它不会花费517天。除非您自己指定,否则您永远无法强制用户创建一个真正随机的密码 - 这也有其自身的问题。 - SilverlightFox
这就是为什么我们选择了一个具有可配置迭代次数的算法。随着摩尔定律的发展,您可以增加迭代次数。 - SilverlightFox
是的!人类不是随机的,我记得我在2或3年前读过一篇关于这个的文章,但我完全忘记了。谢谢你提醒我。现在我保证我不会忘记为什么MD5不安全:1. MD5非常快 2. 人类不是随机的。非常感谢你的帮助。 - Masoud

6
MD5的问题在于它非常快,使用普通硬件可以计算出约9亿个MD5/s。要用暴力破解整个英语词典(大约20万个单词),只需要不到一毫秒的时间。这就是为什么像BCrypt这样的适当哈希算法提供了一个成本因素。成本因素定义了计算哈希所需的时间,并且可以在未来增加。50毫秒的登录时间几乎不是问题,但对于暴力破解来说却是致命的。

1
我一直认为即使是好的CPU,我们也只能做出几千个MD5/s。但是9 Giga MD5/s真的太疯狂了! - Masoud
1
@Masoud - 这确实是一个令人印象深刻的数字,需要注意的是,链接的软件使用的是GPU而不是CPU。但是无论如何,每个人都可以购买一些廉价的GPU并开始比赛... - martinstoeckli
1
@Masoud - 还要看一下推导(链接页面上方的几行)。在那里,您可以看到破解工具提供了很多可能的组合,例如 md5($salt.$pass.$salt) - martinstoeckli
1
是的。使用一张约200美元的GPU,我们可以每秒计算约85亿个哈希值。 对于一个包含大小写字母和数字字符的10个字符长度的密码,即使使用此软件,在平均情况下也需要13714小时才能破解它;而对于一个12个字符长度的密码,平均需要6000年才能破解一个单一的哈希值。这不足以证明更复杂的哈希算法并不能很好地保护用户密码吗?这些算法是否会根据摩尔定律很快成为另一个MD5呢?因此,还是强制用户选择更安全的、有更好强度的密码而不是使用更复杂的哈希算法更好呢? - Masoud
3
@Masoud - 一个好问题。 • 不会成为下一个MD5,因为成本因素的原因。将该因素增加1将使计算哈希所需的时间加倍。 • 你无法强制用户使用更强大的密码,但可以鼓励他们这样做。复杂的密码规则会与好的密码方案产生冲突,而用户在使用密码方面非常有创造力:Password-2015可以满足大多数规则,但是它是一个非常弱的密码。即使是弱密码也应尽可能得到保护,即使它们不被推荐。 - martinstoeckli
显示剩余3条评论

0
您谈到减慢验证的问题,但这是防止泄漏哈希和暴力攻击的唯一防御。现代解决方案会重复哈希计算(即:数千次),以提高计算成本。

尼尔,我认为一个慢算法并不是很安全。我有两个原因,一个在问题(DDOS攻击)中解释,另一个在@martinstoeckli的答案中评论。你能不能看一下,告诉我我错了还是没错? - Masoud
你有DDOS参考链接吗? - Neil Smithline

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