SHA1、md5和SHA256:在PHP登录中应该使用哪个?

136

我正在制作一个PHP登录系统,我在考虑使用SHA1、MD5还是另一篇Stackoverflow文章中提到的SHA256。它们中有哪个更安全?对于SHA1/256,我是否仍要使用盐?

此外,将密码以哈希方式存储在MySQL中是否安全?

function createSalt()
{
    $string = md5(uniqid(rand(), true));
    return substr($string, 0, 3);
}

$salt = createSalt();

$hash = sha1($salt . $hash);

1
请查看https://dev59.com/C3I-5IYBdhLWcg3w99kH#1592620 - Asaph
请参考这个答案,并阅读有关密码哈希的部分。 - Ja͢ck
11个回答

116
不要使用上述提到的哈希算法,应该使用bcrypt。这些哈希算法都被优化为在硬件上快速和容易,因此破解它们具有相同的特点。如果没有其他选择,至少确保使用一个长的盐并多次重新哈希。
在PHP 5.5+中使用bcrypt
PHP 5.5提供了新的密码哈希函数。这是现代Web应用程序中建议的密码存储方法。
// Creating a hash
$hash = password_hash($password, PASSWORD_DEFAULT, ['cost' => 12]);
// If you omit the ['cost' => 12] part, it will default to 10

// Verifying the password against the stored hash  
if (password_verify($password, $hash)) {
    // Success! Log the user in here.
}

如果您使用的是较旧版本的PHP,您应该升级, 但在此之前,您可以使用password_compat来公开此API。
此外,请让password_hash()为您生成盐。它使用CSPRNG
bcrypt的两个注意事项:
1. Bcrypt会默默地截断超过72个字符的任何密码。 2. 在任何NUL字符后,Bcrypt都将进行截断。
(这里有两个注意事项的Proof of Concept。)
您可能会尝试通过在运行它们通过bcrypt之前对密码进行预散列来解决第一个问题,但这样做可能会使您的应用程序陷入第二个问题。
不要编写自己的方案,而是使用已由安全专家编写和/或评估的现有库。

TL;DR - 使用bcrypt


3
是的。我在提到SHA1、SHA256和MD5以及一系列优化速度的其他哈希算法。你不想使用速度优化的哈希算法来保护密码。有许多好的文章讨论了为什么,我特别喜欢这篇文章:http://chargen.matasano.com/chargen/2007/9/7/enough-with-the-rainbow-tables-what-you-need-to-know-about-s.html - Johannes Gorset
4
自 PHP 5.3 版本起,“crypt”函数已包含在内。如果你使用较早版本的PHP,建议考虑使用“phpass”框架来实现类似功能。 - Johannes Gorset
1
所以,如果我的密码是'zebra':crypt(zebra) -> 存储在数据库中。在登录页面上,用户输入'zebra',我只需检查'crypt('zebra')= stored_hash_for_this_user'即可。 - Tony Stark
3
@Stanislav Palatnik表示:SHA512是一个很好的替代选择。不要误解我,我并不是说“拉伸和加盐”的SHA512哈希不安全。它是安全的。即便如此,事实仍然存在,bcrypt比它更安全,因此我认为没有理由不使用它。 - Johannes Gorset
11
bcrypt 的设计初衷是为了降低破解的速度,从而增加破解的困难。 - Johannes Gorset
显示剩余20条评论

22

我认为使用 md5 或 sha256 或任何针对速度优化的哈希函数都是完全可以接受的,非常想听听其他用户可能会有的反驳意见。以下是我的理由:

  1. 如果允许用户使用弱密码,如上帝、爱情、战争、和平等,那么无论加密方式如何,用户仍然可以输入密码而非哈希值,这些弱密码通常首先被使用,因此这与加密没有任何关系

  2. 如果你不使用 SSL 或没有证书,则监听流量的攻击者将能够获取密码,并且像 JavaScript 等客户端加密的尝试很容易被破解和克服。同样,这也与服务器端数据加密没有任何关系

  3. 暴力破解攻击将利用弱密码,而且因为你允许用户输入数据,如果你没有登录限制,比如 3 次或稍微多一点,那么问题仍然与数据加密无关

  4. 如果你的数据库遭到破坏,那么最有可能的情况是所有东西都被破坏了,包括你所采用的哈希技术,不管你把它做得多么难以破解。这可能是一次不满的员工 XSS 攻击、SQL 注入或其他攻击,与密码加密没有任何关系

我确实认为你应该仍然加密,但我所能看到的唯一作用就是防止已经获得或某种方式获得了数据库访问权限的人直接读出密码。如果是未经授权对数据库的访问,那么你有更大的问题要担心,这也是为什么 Sony 被攻击的原因,因为他们认为加密密码可以保护包括信用卡号在内的所有信息,其实只保护了一个字段而已。

我认为在数据库中使用复杂的加密技术仅有一个纯粹的好处,那就是延迟那些具有访问数据库权限的员工或其他人从中读取密码。所以如果这只是一个小项目,我不会过于担心服务器端的安全性,而是更关注客户端可能发送到服务器的任何内容的安全性,例如SQL注入、XSS攻击或其他可能导致你被攻击的方法。如果有人不同意我的观点,我期待着阅读一种从客户端角度来说超级加密密码是必需的方法。

我想要澄清的原因是因为人们经常误以为加密密码意味着他们不必再担心密码的安全问题,从而不再关注网站的安全性。


1
说得好。网络安全应该优先考虑,它们不仅可以保护您的数据,还可以使服务器防御准备就绪。 - CᴴᴀZ
15
这个说法真的误导人。当然,在数据库中安全地哈希密码并不能提高应用程序级别或数据库级别的安全性。这不是安全的万能药。你想要在数据库中安全地哈希用户密码有两个原因:首先,客户信任您的密码,并且他们可能会在其他网站上使用该密码,因此即使您的数据库被攻击,也要确保密码不可恢复;其次,您希望在安全漏洞的情况下消除责任。我不知道有没有相关的诉讼案例,但泄露密码会给公司带来非常糟糕的形象。 - James McMahon
7
这是错误的建议。如果有人窃取了你的数据库并获取了所有的哈希密码,即使他们还攻击了数据库的其他部分,防止他们破解密码并登录仍然非常重要。(例如想想银行网站。)速度优化的算法使攻击者能够针对窃取的哈希值测试许多候选密码,直到找到匹配的。诸如bcrypt和scrypt之类的算法使得测试候选密码与哈希的过程变得缓慢和昂贵,即使他们知道你使用的是哪种算法。因此,如果有人窃取了哈希,它们可以提供更好的保护。 - Richard
7
如果您的数据库被攻破,那么最有可能的情况是所有东西都已经被攻破,包括您所使用的加密技术,无论您将它做得多么难以理解。这就是为什么bcrypt如此重要的原因。使用bcrypt进行加密,即使有人获得了散列值也不会太过危险。但如果使用像SHA1/MD5这样的快速哈希算法,则会存在风险。 - ceejayoz
我认为你们中的一些人忽略了一个重点,那就是当所有其他数据都是明文时,密码即使安全到1000%也没有意义。例如,如果整个数据库被攻破,黑客现在拥有了所有明文信用卡号码。是的,许多人在不同的网站上使用相同的密码,但他们已经被告知不要这样做了,所以这不再是我们的问题。如果数据库本身包含敏感信息,并且其受损是一个问题,那么应该加密整个数据库,尽可能强地加密。 - UncaAlby
这个答案可能基于2011年的实用主义,但现在,在PHP中强密码哈希算法(bcrypt/Argon2)是如此容易获取,没有理由不在新项目中使用它们。我们有责任对我们的用户负责,不能随意处理他们的数据。 - dsturbid

14
正如Johannes Gorset所指出的,Matasano Security的Thomas Ptacek的帖子解释了为什么简单通用的哈希函数(如MD5、SHA1、SHA256和SHA512)是糟糕的密码哈希选择。
为什么呢?它们太快了--使用现代计算机,每个核心每秒可以计算至少1,000,000个MD5哈希值,因此对大多数人使用的密码进行暴力破解是可行的。而且这远远不及基于GPU的破解服务器集群!
没有关键拉伸的盐只意味着您无法预先计算彩虹表,您需要为该特定盐构建它。但它并不会让事情变得更加困难。
用户@Will说:

每个人都在谈论这件事,就好像他们可以在互联网上被黑客攻击一样。正如已经声明的那样,限制尝试次数使得在互联网上破解密码成为不可能,与哈希无关。

他们不需要这样做。显然,在LinkedIn的案例中,他们利用了常见的SQL注入漏洞来获取登录DB表并离线破解了数百万个密码。
然后他回到了离线攻击场景:
安全性真正发挥作用的时候是整个数据库被攻破,黑客可以对md5哈希进行每秒100 million次密码尝试。SHA512慢大约10,000倍。 不,SHA512并非比MD5慢10000倍——它只需要大约两倍的时间。另一方面,Crypt/SHA512则是一个非常不同的东西,就像其BCrypt对应物一样,执行key stretching,生成一个具有随机盐的非常不同的哈希值,并且需要花费500到999999倍的计算时间(可调整)。
SHA512 => aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d
Crypt/SHA512 => $6$rounds=5000$usesomesillystri$D4IrlXatmP7rx3P3InaxBeoomnAihCKRVQP22JZ6EY47Wc6BkroIuUUBOov1i.S5KPgErtP/EN5mcO.ChWQW21

因此,对于PHP来说,选择是使用Crypt/Blowfish(BCrypt)、Crypt/SHA256或 Crypt/SHA512。或者至少使用Crypt/MD5(PKH)。请参见www.php.net/manual/en/function.crypt.php


简单的密码哈希是可以的,服务器的安全取决于防火墙的保护。所谓的“防火墙”是指能够阻止/减缓暴力破解攻击的反应式防火墙(人类只能打那么快)……这场比赛毫无意义……“如果某个黑客闯进来并拿到了所有东西怎么办”(摔桌子)——如果黑客入侵了您的服务器,那就完了。 密码大多数情况下被破解是因为它们太简单了,或者软件开发人员太懒了,没有像网络犯罪分子一样思考。 - user4244405
@argon 请再仔细阅读。哈希密码的整个目的是为了在您的登录数据库被攻击时(它迟早会被攻击),您不会立即泄漏所有用户的密码,这些密码通常由用户在不同的网站上重复使用 - 否则,输入后的简单延迟就足够了。而且,“如果他们进入了你的服务器,游戏就结束了”这一点是无关紧要的,因为他们不需要进入您的服务器。黑客最常用的漏洞是SQL注入(参见Linkedin案例),然后他们会暴力破解哈希值。这就是为什么您需要加盐和拉伸的原因。 - LexLythius
如果安全要求真的如此严格,那么将密码存储在其他地方可能是一个好主意。有许多方法来保护您的数据(和源代码)-针对“如果/当”...-但是将密码哈希化为“亿万位”只有在以下情况下才是安全的:密码创建者/客人系统、数据传输以及服务于服务器硬件的人类。话虽如此:我并不是说哈希是无用的,但我想说的是,如果您的解决方案只是更复杂的哈希,那么恐怕它在不久的将来不会奏效,尤其是考虑到量子计算机的出现。 - user4244405
递归哈希会增加任何黑客攻击的工作/成本。迭代次数越多,破解难度就越大,因此即使是像SHA256这样快速的哈希也可以使用。迭代必须是随机的,并且可以公开。password_hash()在哈希本身中显示成本。 - Victor Stoddard
@VictorStoddard 是的,你可以自己编写哈希方案并提供可调节的密钥延展性。但是,为什么不使用已经存在的、经过更加深入审查的算法,这些算法是专家们为恰好这个目的而创建和提供的呢? - LexLythius
@LexLythius password_hash() 是行业标准,我并不建议替换它。我是在说快速哈希算法,比如 sha256,可以通过增加成本来降低速度 -- 在这种情况下,就是递归哈希。在 password_hash() 开发之前,这是很常见的做法。 - Victor Stoddard

13

使用SHA256。虽然SHA512对于快速哈希来说更理想,但在这些选项中,它是明确的选择。就像任何哈希技术一样,请务必为其添加盐以增加安全性。

另外,FRKT,请告诉我在哪里可以轻松破解加盐的SHA256哈希?我真的非常感兴趣。

重要编辑:

从现在开始,请使用bcrypt作为强化的哈希。更多信息可以在这里找到


有关盐的编辑:

使用随机数或随机字节流等。您还可以使用数据库中记录的唯一字段作为盐,这样每个用户的盐都不同。


1
但它们被优化为速度,这意味着它们可以启用暴力破解。 - arbales
6
事实上,这是为了保护密码。通过使用带盐的哈希函数,并在网站上添加3次尝试限制,可以大大减慢黑客的尝试(即使是暴力破解)。如果使用任何纯加密方法,您现在又有了另一个问题——保护密钥。如果密钥被发现,您的整个数据库就会受到威胁(如果没有添加盐)。然而,哈希函数的原始密码将永远无法从系统中获得,这正是应该的。 - Kyle Rosendo
1
正如所指出的,MD和SHA系列的问题在于它们被优化为速度。随着计算技术的发展,这类哈希算法越来越容易受到安全威胁。 - Johannes Gorset
1
什么是在PHP中生成盐的好方法?有示例代码吗?我想我不应该随便选择像“长颈鹿”这样的东西。 - Tony Stark
1
@Kyle Rozendo,您的观点是错误的。Bcrypt不会因为计算机技术的进步而变得不安全,这正是您应该使用它的原因。 - Johannes Gorset
显示剩余14条评论

4
人们似乎忽略了一个问题,如果黑客可以访问数据库,那么他很可能也可以访问用于哈希密码的php文件,并且可以修改该文件以获取所有成功的用户名和密码组合。如果他无法访问Web目录,他仍然可以选择密码哈希并将其写入数据库中。换句话说,哈希算法并不像系统安全性和限制登录尝试次数那样重要,如果您不使用SSL,则攻击者可以监听连接以获得信息。除非你需要算法花费很长时间来计算(出于你自己的目的),否则SHA-256或SHA-512与用户特定盐应该足够了。
为了增强安全性,请设置一个脚本(bash、批处理、python等),给它起一个晦涩的名称,并检查并查看login.php是否有更改(检查日期/时间戳),如果有更改则向您发送电子邮件。此外,建议记录具有管理员权限的所有登录尝试,并记录所有尝试登录到数据库的失败尝试,并将日志发送电子邮件给您。

1
人们似乎忽略了一个事实,即如果黑客可以访问数据库,他很可能也可以访问哈希密码的PHP文件...但这并不是真的。最常见的漏洞之一是SQL注入,它会给予对数据库的读写权限,但无法访问PHP代码。 - ceejayoz
如果您可以访问代码,则可以修改并存储用户发送的所有密码,以明文形式保存。因此,如果代码被攻击,则游戏结束。 - magallanes

3

大家都在谈论这个问题,好像他们可以通过互联网被黑客攻击。正如已经说明的那样,限制尝试次数使得破解密码变得不可能,并且与哈希无关。

盐是必须的,但复杂性或多个盐甚至并不重要。任何一个盐都可以防止攻击者使用预先制作的彩虹表。每个用户的唯一盐可以防止攻击者创建新的彩虹表来攻击您的整个用户群。

当整个数据库被攻击者窃取,并且黑客可以对md5哈希执行1亿次密码尝试时,安全性真正发挥作用。SHA512大约慢了10,000倍。即使使用今天的计算能力,一个复杂的密码在md5下仍然需要100年才能被暴力破解,而在SHA512下则需要10,000倍的时间。盐根本不能阻止暴力破解,因为它们总是必须被知道的,如果攻击者下载了您的数据库,他可能已经进入了您的系统。


2
这里是MD5和SHA1的比较。你可以清楚地了解哪个更好。 enter image description here

1
使用argon2i。密码哈希函数argon2赢得了密码哈希竞赛。
如果不可用,其他合理的选择是scryptbcryptPBKDF2。维基百科有这些函数的页面: MD5、SHA1和SHA256是信息摘要,而不是密码哈希函数。它们不适用于此目的。 从MD5切换到SHA1或SHA512并不能显著提高构造物的安全性。计算SHA256或SHA512哈希非常快。攻击者可以使用普通硬件每秒尝试数千万(使用单个CPU)甚至数十亿(使用单个GPU)个哈希。好的密码哈希函数包括一个工作因素来减慢字典攻击速度。 这里有一个建议,供PHP程序员使用:阅读 PHP FAQ,然后使用password_hash()

1

MD5因碰撞问题而不安全 - 两个不同的密码可能会生成相同的md-5。

对于此类情况,Sha-1已经足够安全。存储加盐的sha-1版本密码的原因是为了让服务器不会保存用户的密码,这些密码可能被用于其他人的服务器。否则,有什么区别呢?

如果黑客以某种方式窃取了您的整个未加密数据库,那么加盐哈希密码唯一的作用就是防止他冒充用户进行未来的登录 - 黑客已经拥有了数据。

如果用户输入的是明文密码,那么对于攻击者来说,拥有哈希值有什么好处呢?

即使黑客使用未来技术每秒可以生成一百万个sha-1密钥进行暴力攻击,您的服务器是否能够处理黑客每秒一百万次的登录尝试来测试他的密钥?这是在您允许黑客尝试使用加盐的sha-1而不是像正常登录一样使用密码进行登录的情况下。

最好的方法是将错误的登录尝试限制在一定数量内 - 例如25次,然后让用户暂停一两分钟。如果24小时内累计的错误登录尝试次数达到250次,则关闭帐户访问权限并向所有者发送电子邮件。


即使我使用较弱的加密方式,实际上没有任何系统能够承受每秒超过100万次的暴力攻击。 - magallanes
1
三次登录失败,您将被锁定。就这样,故事结束了。即使是像“1234”这样超级愚蠢的密码也是安全的,如果黑客碰巧首先尝试“password”、“pa$$word”和“passw0rd”(锁定!)。用户如何恢复访问现在成为您的安全难点,而您所做的取决于您的用户基数大小。200个用户?请让他们打电话寻求帮助。 - UncaAlby
这个答案对别人来说没有意义吗,还是只有我觉得呢? - Wildcard

-1

MD5加密是最糟糕的之一,因为你必须将代码反转并且它已经被解密了。我建议您使用SHA256。我编程时间更长,有很好的经验。下面也会有一个加密。

password_hash() example using Argon2i

<?php
echo 'Argon2i hash: ' . password_hash('rasmuslerdorf', PASSWORD_ARGON2I);
?>
The above example will output something similar to:

Argon2i hash: $argon2i$v=19$m=1024,t=2,p=2$YzJBSzV4TUhkMzc3d3laeg$zqU/1IN0/AogfP4cmSJI1vc8lpXRW9/S0sYY2i2jHT0

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