ChatGPT解决这个技术问题 Extra ChatGPT

PHP 密码的安全哈希和盐

目前据说MD5部分不安全。考虑到这一点,我想知道使用哪种机制来保护密码。

这个问题,Is “double hashing” a password less secure than just hashing it once? 表明多次散列可能是一个好主意,而 How to implement password protection for individual files? 建议使用盐。

我正在使用 PHP。我想要一个安全快速的密码加密系统。将密码散列一百万次可能更安全,但也更慢。如何在速度和安全之间取得良好的平衡?另外,我希望结果具有恒定数量的字符。

散列机制必须在 PHP 中可用 必须安全 可以使用盐(在这种情况下,所有盐都一样好吗?有什么方法可以生成好的盐吗?)

另外,我是否应该在数据库中存储两个字段(例如,一个使用 MD5,另一个使用 SHA)?它会让它更安全还是更不安全?

如果我不够清楚,我想知道要使用哪个散列函数以及如何选择好的盐,以便拥有安全快速的密码保护机制。

不完全涵盖我的问题的相关问题:

What's the difference between SHA and MD5 in PHP
Simple Password Encryption
Secure methods of storing keys, passwords for asp.net
How would you implement salted passwords in Tomcat 5.5

openwall.com/phpass 也是很好的库
Md5 现在完全不安全
@NSAwesomeGuy 这取决于您使用它的目的。当然,彩虹匹配或只是暴力破解未加盐的 MD5 密码是微不足道的,但是通过适当的盐渍,建立一个彩虹表来快速破解密码集仍然是非常不切实际的,而且暴力破解是不可能的。
PHP 5.5+ 具有内置于 php.net/manual/en/function.password-hash.php 的安全密码哈希

A
Aurelio

免责声明:这个答案写于 2008 年。从那时起,PHP 为我们提供了 password_hash 和 password_verify,并且自推出以来,它们就是推荐的密码散列和检查方法。答案的理论仍然是一个很好的阅读。

TL;博士

不要做

不要限制用户可以输入哪些字符作为密码。只有白痴才会这样做。

不要限制密码的长度。如果您的用户想要一个带有 supercalifragilisticexpialidocious 的句子,请不要阻止他们使用它。

不要去除或转义密码中的 HTML 和特殊字符。

切勿以纯文本形式存储用户密码。

永远不要通过电子邮件向您的用户发送密码,除非他们丢失了密码,并且您发送了一个临时密码。

永远不要以任何方式记录密码。

切勿使用 SHA1 或 MD5 甚至 SHA256 散列密码!现代破解器可以超过 60 和 1800 亿哈希/秒(分别)。

不要混合 bcrypt 和 hash() 的原始输出,要么使用十六进制输出,要么使用 base64_encode。 (这适用于任何可能包含流氓 \0 的输入,这会严重削弱安全性。)

多斯

尽可能使用 scrypt; bcrypt 如果你不能。

如果您不能使用 bcrypt 或 scrypt 以及 SHA2 哈希,请使用 PBKDF2。

当数据库被破坏时重置每个人的密码。

实现一个合理的 8-10 个字符的最小长度,加上至少需要 1 个大写字母、1 个小写字母、一个数字和一个符号。这将提高密码的熵,从而使其更难破解。 (有关辩论,请参阅“什么是好的密码?”部分。)

为什么要散列密码呢?

散列密码背后的目标很简单:通过破坏数据库来防止恶意访问用户帐户。因此,密码散列的目标是通过花费太多时间或金钱来计算纯文本密码来阻止黑客或破解者。时间/成本是您武器库中最好的威慑力量。

您希望用户帐户具有良好、强大的散列的另一个原因是给您足够的时间来更改系统中的所有密码。如果您的数据库受到威胁,您将需要足够的时间来至少锁定系统,如果不更改数据库中的每个密码的话。

Jeremiah Grossman,Whitehat Security 的首席技术官,stated on White Hat Security blog 在最近一次需要暴力破解密码保护的密码恢复之后:

有趣的是,在经历这场噩梦的过程中,我学到了很多我不知道的密码破解、存储和复杂性。我开始明白为什么密码存储比密码复杂性重要得多。如果您不知道密码是如何存储的,那么您真正可以依赖的只是复杂性。这可能是密码和加密专业人士的常识,但对于普通的 InfoSec 或 Web 安全专家来说,我非常怀疑。

(强调我的。)

什么才是好的密码?

Entropy。 (并不是说我完全赞同兰德尔的观点。)

简而言之,熵是密码中有多少变化。当密码只有小写罗马字母时,只有 26 个字符。那变化不大。字母数字密码更好,有 36 个字符。但是允许大小写,带符号,大约是 96 个字符。这比单纯的字母好多了。一个问题是,为了让我们的密码容易记住,我们插入了模式——这会降低熵。哎呀!

密码熵很容易approximated。使用全范围的 ascii 字符(大约 96 个可键入字符)会产生每个字符 6.6 的熵,对于未来的安全性来说,8 个字符的密码仍然太低(52.679 位的熵)。但好消息是:更长的密码和带有 unicode 字符的密码,确实会增加密码的熵并使其更难破解。

Crypto StackExchange 网站上有关于密码熵的较长讨论。一个好的谷歌搜索也会出现很多结果。

在我与@popnoodles 交谈的评论中,他指出使用 X 多个字母、数字、符号等执行 X 长度的密码策略实际上可以通过使密码方案更可预测来减少熵。我同意。尽可能真正随机的随机性始终是最安全但最不令人难忘的解决方案。

据我所知,制作世界上最好的密码是 Catch-22。要么是难以记住、太容易预测、太短、太多 unicode 字符(在 Windows/Mobile 设备上难以输入)、太长等。没有任何密码能真正满足我们的目的,因此我们必须像保护它们一样保护它们在诺克斯堡。

最佳实践

Bcrypt 和 scrypt 是当前的最佳实践。 Scrypt 在时间上会比 bcrypt 更好,但它还没有被 Linux/Unix 或网络服务器作为标准采用,并且还没有发布对其算法的深入评论。但是,该算法的未来确实看起来很有希望。如果您正在使用 Ruby,有一个 scrypt gem 可以帮助您,Node.js 现在有自己的 scrypt 包。您可以通过 Scrypt 扩展或 Libsodium 扩展(两者都在 PECL 中可用)在 PHP 中使用 Scrypt。

如果您想了解如何使用 bcrypt,或者为自己找到一个 good wrapper 或使用类似 PHPASS 的东西来实现更旧的实现,我强烈建议您阅读 crypt function 的文档。我建议至少 12 轮 bcrypt,如果不是 15 到 18 轮。

当我了解到 bcrypt 只使用带有可变成本机制的河豚密钥计划时,我改变了使用 bcrypt 的想法。后者让您通过增加河豚已经很昂贵的密钥计划来增加暴力破解密码的成本。

平均做法

我几乎无法再想象这种情况了。 PHPASS 支持 PHP 3.0.18 到 5.3,因此几乎可以在所有可以想象的安装中使用它,如果您确定您的环境支持 bcrypt,则应该使用它。

但是假设你根本不能使用 bcrypt 或 PHPASS。然后怎样呢?

尝试使用您的环境/应用程序/用户感知可以容忍的 maximum number of rounds 实现 PDKBF2。我推荐的最低数量是 2500 发。此外,如果可以使操作更难重现,请确保使用 hash_hmac()

未来实践

PHP 5.5 中的 full password protection library 可以消除使用 bcrypt 的任何痛苦。虽然我们大多数人在最常见的环境中(尤其是共享主机)都坚持使用 PHP 5.2 和 5.3,但@ircmaxell 为即将到来的 API 构建了一个 compatibility layer,它向后兼容 PHP 5.3.7。

密码学回顾和免责声明

实际破解散列密码所需的计算能力并不存在。计算机“破解”密码的唯一方法是重新创建密码并模拟用于保护密码的哈希算法。散列的速度与其被暴力破解的能力呈线性关系。更糟糕的是,大多数哈希算法可以轻松并行化以更快地执行。这就是为什么像 bcrypt 和 scrypt 这样昂贵的方案如此重要的原因。

您不可能预见所有威胁或攻击途径,因此您必须尽最大努力预先保护您的用户。如果你不这样做,那么你甚至可能会错过你被攻击的事实,直到为时已晚......你要承担责任。为了避免这种情况,一开始就表现得偏执。攻击您自己的软件(内部)并尝试窃取用户凭据,或修改其他用户的帐户或访问他们的数据。如果您不测试系统的安全性,那么除了您自己之外,您不能责怪任何人。

最后:我不是密码学家。我所说的都是我的观点,但我碰巧认为它是基于良好的常识......以及大量阅读。请记住,尽可能多疑,让事情尽可能难以入侵,然后,如果您仍然担心,请联系白帽黑客或密码学家,看看他们对您的代码/系统的看法。


秘密无济于事,因为您的密码数据库无论如何都应该是秘密的-如果他们可以掌握该数据库,他们还可以找到您正在使用的任何秘密。然而重要的是盐是随机的。
@wicked 跳蚤,我不是在和你争论。只是指出我们工作的这个领域是多么令人费解和复杂。我一直希望通过建立小型网站的内容管理系统的最聪明、最聪明的最佳实践来学习。我还在这儿学习。 ...每次我读到有意义的东西时,我很快就会注意到其他 5 篇与之相矛盾的帖子。那一圈又一圈很快就让人头晕目眩:)
有趣的修订。用户 ID(例如,自动增量 BIGINT)是一个好的随机数吗?还是因为它不是随机的所以不好?另外,我必须将每个用户的随机数存储在数据库中......站点密钥 + 随机数 + HMAC 是否比多次迭代的加盐(使用用户 ID)哈希提供了显着改进的安全性?同样,多次迭代 HMAC 对安全性有好处吗?
通过电子邮件发送一个临时密码,要求用户在第一次使用它时更改它,并通过电子邮件发送一个允许他们设置密码的“安全”链接同样有风险。在任何一种情况下,只要在预期收件人之前使用链接或密码,任何拦截电子邮件的人都可以访问该帐户。
@RobertK通过扩展字符集是的,它增加了,但是通过强制所有密码遵循规则减少了可能选项的数量。假设有人要通过蛮力获取密码。通过告诉他们用户的密码有 1 个大写字母、1 个小写字母、一个数字和一个符号,这意味着他们需要的尝试次数要少得多。通过允许用户决定他们想要什么,黑客必须更加努力。
R
RichVel

一个更短更安全的答案 - 根本不要编写自己的密码机制,使用久经考验的机制。

PHP 5.5 或更高版本:password_hash() 质量很好,是 PHP 核心的一部分。

PHP 4.x(过时):OpenWall 的 phpass 库比大多数自定义代码要好得多 - 用于 WordPress、Drupal 等。

大多数程序员只是不具备在不引入漏洞的情况下安全地编写与加密相关的代码的专业知识。

快速自检:什么是密码延伸以及您应该使用多少次迭代?如果您不知道答案,您应该使用 password_hash(),因为密码拉伸现在是密码机制的一个关键特性,因为 CPU 速度更快,并且使用 GPUs and FPGAsbillions of guesses per second 的速率破解密码(使用GPU)。

截至 2012 年,您可以crack all 8-character Windows passwords in 6 hours使用 5 台台式电脑中安装的 25 个 GPU。这是暴力破解,即枚举和检查每个 8 字符的 Windows 密码,包括特殊字符,而不是字典攻击。使用现代 GPU,您当然可以破解更多密码或使用更少的 GPU - 或者以合理的成本在云中租用 GPU 几个小时。

也有很多针对 Windows 密码的彩虹表攻击,运行在普通 CPU 上,速度非常快。

这一切都是因为 Windows 仍然 doesn't salt or stretch它的密码,even in Windows 10。在 2021 年仍然如此。不要犯和微软一样的错误!

也可以看看:

很好的答案,更多关于为什么 password_hash() 或 phpass 是最好的方法。

不错的博客文章,为主要算法(包括 bcrypt、scrypt 和 PBKDF2)提供了推荐的“工作因素”(迭代次数)。


但这些系统更为人所知,可能已经受到损害。但是当你不知道自己在做什么时,它胜过自己制作。
关于“这些系统更广为人知,并且可能已经受到损害” - 没有理由仅仅因为它更广为人知而设计良好的身份验证系统就应该成为“已经受到损害”。诸如 phpass 之类的库是由专家编写并由许多人详细审查的——它们众所周知的事实伴随着不同人的详细审查,并且更有可能意味着它们是安全的。
“根本不要编写自己的密码机制”——但真正偏执的人会希望自己编写密码机制,以尽量减少 NSA 拥有后门的可能性。
@PP - 在我看来,同行评审的密码哈希算法具有 NSA 后门的可能性非常低。不是真正的加密专家的人编写没有其他漏洞的新密码哈希机制的机会要低得多。典型的 web 应用程序只使用 MD5 或 SHA-1 哈希,这很糟糕——甚至 Chris Shiflett 的其他很棒的 Essential PHP Security 书也推荐 MD5 ...
phpass 不是最好的方法。从来没有,而且很可能永远不会。几年前我查看了代码,它在 Windows 或 /dev/urandom 不可用的任何平台上都不安全。在安全性方面,它不遵循最佳实践,在应该终止应用程序而不是对安全性做出虚假声明时使用 md5() 和 microtime() 的组合。尽管 PHP 本身在安全领域以 bcrypt 为核心,但自从我查看代码以来,它还没有看到任何更新。远离 phpass。
T
Tom Haigh

我不会以两种不同的方式存储散列密码,因为这样系统至少与使用的最弱散列算法一样弱。


A
AlliterativeAlice

自 PHP 5.5 起,PHP 具有简单、安全的函数,用于散列和验证密码 password_hash()password_verify()

$password = 'anna';
$hash = password_hash($password, PASSWORD_DEFAULT);
$expensiveHash = password_hash($password, PASSWORD_DEFAULT, array('cost' => 20));

password_verify('anna', $hash); //Returns true
password_verify('anna', $expensiveHash); //Also returns true
password_verify('elsa', $hash); //Returns false

当使用 password_hash() 时,它会生成一个随机盐并将其包含在输出的哈希中(连同使用的成本和算法)。password_verify() 然后读取该哈希并确定使用的盐和加密方法,并针对它进行验证提供的明文密码。

提供 PASSWORD_DEFAULT 指示 PHP 使用已安装 PHP 版本的默认散列算法。确切地说,这意味着哪种算法打算在未来的版本中随着时间的推移而改变,因此它将始终是最强大的可用算法之一。

增加成本(默认为 10)使哈希更难暴力破解,但也意味着生成哈希并针对它们验证密码将为您的服务器的 CPU 做更多的工作。

请注意,即使默认散列算法可能会更改,旧的散列仍将继续验证,因为使用的算法存储在散列中,password_verify() 会使用它。


i
infojolt

尽管问题已得到解答,但我只想重申,用于散列的盐应该是随机的,而不是像第一个答案中建议的电子邮件地址。

更多解释可在 - http://www.pivotalsecurity.com/blog/password-hashing-salt-should-it-be-random/

最近我讨论了用随机位加盐的密码哈希是否比用可猜测或已知盐加盐的密码哈希更安全。让我们看看:如果存储密码的系统以及存储随机盐的系统被破坏,攻击者将可以访问哈希和盐,所以盐是否是随机的,并不重要。攻击者可以生成预先计算好的彩虹表来破解哈希。有趣的部分来了——生成预先计算的表并不是那么简单。让我们以 WPA 安全模型为例。您的 WPA 密码实际上从未发送到无线接入点。相反,它使用您的 SSID(网络名称,如 Linksys、Dlink 等)进行哈希处理。这里有一个很好的解释它是如何工作的。为了从哈希中检索密码,您需要知道密码以及盐(网络名称)。 Church of Wifi 已经预先计算了哈希表,其中包含前 1000 个 SSID 和大约 100 万个密码。所有表的大小约为 40 GB。正如您在他们的网站上看到的,有人使用 15 个 FGPA 阵列 3 天来生成这些表。假设受害者使用 SSID 为“a387csf3”,密码为“123456”,会被那些表破解吗?不! .. 这不可以。即使密码很弱,这些表也没有 SSID a387csf3 的哈希值。这就是随意加盐的美妙之处。它将阻止那些在预先计算的表格上茁壮成长的饼干。它可以阻止一个坚定的黑客吗?可能不是。但是使用随机盐确实提供了额外的防御层。当我们讨论这个话题时,让我们讨论在单独的系统上存储随机盐的额外优势。场景 #1:密码哈希存储在系统 X 上,用于哈希的盐值存储在系统 Y 上。这些盐值是可猜测的或已知的(例如用户名)场景 #2:密码哈希存储在系统 X 上,盐值用于散列存储在系统 Y 上。这些盐值是随机的。如果系统 X 被破坏,你可以猜到,在单独的系统上使用随机盐有一个巨大的优势(场景 #2)。攻击者需要猜测附加值才能破解哈希。如果使用 32 位盐,则猜测的每个密码可能需要 2^32= 4,294,967,296(约 42 亿)次迭代。


即使攻击者得到了盐,“sitesalt:usersalt:password”字符串仍然可以抵抗预先计算的表,因为攻击者需要为每个用户生成表(因此攻击变得慢得多),当然除非特定用户被针对...
关于“即使攻击者得到了盐,“sitesalt:usersalt:password”字符串仍然可以抵抗预先计算的表”,完全同意。我的观点是,如果将sitesalt设置为随机且长,将使系统比它(sitesalt)可预测的更安全。我见过一些人推荐使用电子邮件 ID 等作为盐,我不鼓励这样做。
你错过了我最初写的东西。我说要使用随机数,与记录一起存储,加上电子邮件地址。电子邮件地址的添加为黑客提供了额外的熵。从那以后,我改写了我的答案以支持 bcrypt。
h
halfer

我只想指出,PHP 5.5 包含一个 password hashing API,它提供了一个围绕 crypt() 的包装器。此 API 显着简化了散列、验证和重新散列密码散列的任务。作者还发布了一个 compatibility pack(以单个 password.php 文件的形式,您只需 require 即可使用),供那些使用 PHP 5.3.7 及更高版本并希望立即使用它的用户使用。

它目前仅支持 BCRYPT,但它旨在轻松扩展以包含其他密码散列技术,并且由于该技术和成本作为散列的一部分存储,因此对您首选的散列技术/成本的更改不会使当前的散列无效,框架验证时会自动使用正确的技术/成本。如果您没有明确定义自己的盐,它还会处理生成“安全”盐。

API 公开了四个函数:

password_get_info() - 返回有关给定哈希的信息

password_hash() - 创建一个密码哈希

password_needs_rehash() - 检查给定的哈希是否与给定的选项匹配。检查散列是否符合您当前的技术/成本方案很有用,允许您在必要时重新散列

password_verify() - 验证密码是否匹配哈希

目前,这些函数接受 PASSWORD_BCRYPT 和 PASSWORD_DEFAULT 密码常量,它们目前是同义词,不同之处在于 PASSWORD_DEFAULT“可能会在更新的 PHP 版本中发生变化,当支持更新、更强的散列算法时”。在登录时使用 PASSWORD_DEFAULT 和 password_needs_rehash() (并在必要时重新散列)应该确保您的散列对蛮力攻击具有相当的弹性,对您几乎没有任何工作。

编辑:我刚刚意识到罗伯特 K 的回答中简要提到了这一点。我将把这个答案留在这里,因为我认为它提供了更多关于它如何工作以及它为那些不了解安全性的人提供的易用性的信息。


r
rabudde

我正在使用 Phpass,它是一个简单的单文件 PHP 类,几乎可以在每个 PHP 项目中轻松实现。另见The H

默认情况下,它使用在 Phpass 中实现的最强可用加密,即 bcrypt 并回退到 MD5 的其他加密,以提供对 Wordpress 等框架的向后兼容性。

返回的哈希可以按原样存储在数据库中。生成哈希的示例用法是:

$t_hasher = new PasswordHash(8, FALSE);
$hash = $t_hasher->HashPassword($password);

要验证密码,可以使用:

$t_hasher = new PasswordHash(8, FALSE);
$check = $t_hasher->CheckPassword($password, $hash);

w
wmfrancia

要记住的事情

关于 PHP 的密码加密已经说了很多,其中大部分都是非常好的建议,但在您开始使用 PHP 进行密码加密的过程之前,请确保您已实施或准备实施以下内容。

服务器

端口

如果您没有正确保护运行 PHP 和 DB 的服务器,那么无论您的加密有多好,您的所有努力都是毫无价值的。大多数服务器的功能相对相同,它们分配了端口以允许您通过 ftp 或 shell 远程访问它们。确保更改您激活的远程连接的默认端口。通过不这样做,您实际上使攻击者在访问您的系统时少了一步。

用户名

对于世界上所有的好东西,不要使用用户名 admin、root 或类似的东西。此外,如果您使用的是基于 unix 的系统,请不要让 root 帐户登录可访问,它应该始终只能是 sudo。

密码

您告诉您的用户使用正确的密码以避免被黑客入侵,也这样做。当后门大开时,努力锁上前门有什么意义。

数据库

服务器

理想情况下,您希望您的数据库和应用程序位于不同的服务器上。由于成本的原因,这并不总是可行的,但它确实可以保证一定的安全性,因为攻击者必须经过两个步骤才能完全访问系统。

用户

始终让您的应用程序拥有自己的帐户来访问数据库,并且只为其提供所需的权限。

然后为您创建一个单独的用户帐户,该帐户不存储在服务器上的任何位置,甚至不在应用程序中。

像往常一样不要做这个根或类似的东西。

密码

遵循与所有良好密码相同的准则。也不要在同一系统上的任何 SERVER 或 DB 帐户上重复使用相同的密码。

PHP

密码

永远不要在你的数据库中存储密码,而是存储哈希和唯一的盐,我稍后会解释原因。

哈希

单向哈希!!!!!!!,永远不要以可以反转的方式对密码进行哈希处理,哈希应该是一种方式,这意味着您不会将它们反转并将它们与密码进行比较,而是对输入的密码进行哈希处理同样的方式并比较两个哈希值。这意味着即使攻击者可以访问数据库,他也不知道实际密码是什么,只知道其生成的哈希值。这意味着在最坏的情况下为您的用户提供更高的安全性。

有很多很好的散列函数(password_hashhash 等),但您需要选择一个好的算法才能使散列有效。 (bcrypt 和类似的算法都是不错的算法。)

当哈希速度是关键时,越慢越能抵抗蛮力攻击。

散列中最常见的错误之一是散列不是用户独有的。这主要是因为盐不是唯一生成的。

密码应始终在散列之前加盐。 Salting 会在密码中添加一个随机字符串,因此类似的密码在数据库中不会出现相同的情况。但是,如果盐不是每个用户唯一的(即:您使用硬编码的盐),那么您几乎会使您的盐一文不值。因为一旦攻击者找出一个密码盐,他就拥有了所有这些盐。

当您创建盐时,请确保它对于盐的密码是唯一的,然后将完成的哈希和盐都存储在您的数据库中。这样做的目的是使攻击者必须单独破解每个盐和哈希,然后才能获得访问权限。这意味着攻击者需要更多的工作和时间。

用户创建密码

如果用户通过前端创建密码,则意味着必须将其发送到服务器。这带来了一个安全问题,因为这意味着未加密的密码被发送到服务器,如果攻击者能够监听和访问,那么您在 PHP 中的所有安全性都是毫无价值的。始终安全地传输数据,这是通过 SSL 完成的,但是即使 SSL 也不是完美无缺的(OpenSSL 的 Heartbleed 缺陷就是一个例子)。

还要让用户创建一个安全密码,这很简单,应该始终这样做,用户最终会感激不尽。

最后,无论您采取什么安全措施都不是 100% 安全的,要保护的技术越先进,攻击就越先进。但是遵循这些步骤将使您的网站更加安全,并且更不适合攻击者追捕。

这是一个 PHP 类,可以轻松地为密码创建哈希和盐

http://git.io/mSJqpw


您应该从不错的哈希算法列表中删除 SHA512,因为它太快了。仅与 PBKDF2 结合使用。虽然 BCrypt 基于河豚,但河豚本身是一种加密算法,而不是散列算法。
您如何将随机盐存储在数据库中?我认为您不会散列它(不能用于验证),也不会明文存储(如果攻击者可以读取数据库,则没有真正的好处)。那么,你是怎么做到的呢?
wmfrancia 写道:“Salting 在密码中添加了一个随机字符串,因此类似的密码在数据库中不会出现相同的情况”。这对我来说没有意义。数据库中的散列已经看起来不同,因为这是散列函数的属性。
wmfancia 在谈到恒定盐时写道:“一旦攻击者找出一个密码盐,他就拥有了所有盐的盐”。同样可以说,如果黑客找出了哪个 DB 字段是盐,那么他就拥有了所有这些字段的盐。由于恒定盐可能不在数据库中,因此恒定盐是一件好事。
当然,这些评论并不是建议每个用户随机使用一种盐并不比每个应用程序一种盐好。这个比较好。
A
AticusFinch

谷歌表示 SHA256 可用于 PHP。

你绝对应该使用盐。我建议使用随机字节(而不是限制自己使用字符和数字)。通常,您选择的时间越长,它就越安全,速度越慢。我猜 64 字节应该没问题。


64位应该对任何人都足够了吗?
@Konerak,我会在 20 年后回到这个问题上。 :) 但是是的,SHA256 确实可用。如果您想了解 SHA256 的安全性,您可能需要查看以下内容:security.stackexchange.com/questions/90064/…
M
Max

最后,从数学上讲,双重哈希并没有带来任何好处。然而,在实践中,它对于防止基于彩虹表的攻击很有用。换句话说,它并不比使用盐进行散列更有好处,后者在您的应用程序或服务器上花费的处理器时间要少得多。


多重哈希还可以防止字典和蛮力攻击 - 即它只会使它们花费更长的时间来计算。
双散列不会给您带来显着优势,但多轮散列迭代仍然是抵御字典和布鲁斯力攻击的可行防御。工业强度密码哈希使用 1000 多轮。 PKCS#5 的 PBKDF1 建议至少 1000 发。
J
Jason OOO

我在这里找到了关于这个问题的完美话题:https://crackstation.net/hashing-security.htm,我希望你从中受益,这里还有源代码,它也提供了对基于时间的攻击的预防。

<?php
/*
 * Password hashing with PBKDF2.
 * Author: havoc AT defuse.ca
 * www: https://defuse.ca/php-pbkdf2.htm
 */

// These constants may be changed without breaking existing hashes.
define("PBKDF2_HASH_ALGORITHM", "sha256");
define("PBKDF2_ITERATIONS", 1000);
define("PBKDF2_SALT_BYTES", 24);
define("PBKDF2_HASH_BYTES", 24);

define("HASH_SECTIONS", 4);
define("HASH_ALGORITHM_INDEX", 0);
define("HASH_ITERATION_INDEX", 1);
define("HASH_SALT_INDEX", 2);
define("HASH_PBKDF2_INDEX", 3);

function create_hash($password)
{
    // format: algorithm:iterations:salt:hash
    $salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTES, MCRYPT_DEV_URANDOM));
    return PBKDF2_HASH_ALGORITHM . ":" . PBKDF2_ITERATIONS . ":" .  $salt . ":" . 
        base64_encode(pbkdf2(
            PBKDF2_HASH_ALGORITHM,
            $password,
            $salt,
            PBKDF2_ITERATIONS,
            PBKDF2_HASH_BYTES,
            true
        ));
}

function validate_password($password, $good_hash)
{
    $params = explode(":", $good_hash);
    if(count($params) < HASH_SECTIONS)
       return false; 
    $pbkdf2 = base64_decode($params[HASH_PBKDF2_INDEX]);
    return slow_equals(
        $pbkdf2,
        pbkdf2(
            $params[HASH_ALGORITHM_INDEX],
            $password,
            $params[HASH_SALT_INDEX],
            (int)$params[HASH_ITERATION_INDEX],
            strlen($pbkdf2),
            true
        )
    );
}

// Compares two strings $a and $b in length-constant time.
function slow_equals($a, $b)
{
    $diff = strlen($a) ^ strlen($b);
    for($i = 0; $i < strlen($a) && $i < strlen($b); $i++)
    {
        $diff |= ord($a[$i]) ^ ord($b[$i]);
    }
    return $diff === 0; 
}

/*
 * PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt
 * $algorithm - The hash algorithm to use. Recommended: SHA256
 * $password - The password.
 * $salt - A salt that is unique to the password.
 * $count - Iteration count. Higher is better, but slower. Recommended: At least 1000.
 * $key_length - The length of the derived key in bytes.
 * $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise.
 * Returns: A $key_length-byte key derived from the password and salt.
 *
 * Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt
 *
 * This implementation of PBKDF2 was originally created by https://defuse.ca
 * With improvements by http://www.variations-of-shadow.com
 */
function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
{
    $algorithm = strtolower($algorithm);
    if(!in_array($algorithm, hash_algos(), true))
        die('PBKDF2 ERROR: Invalid hash algorithm.');
    if($count <= 0 || $key_length <= 0)
        die('PBKDF2 ERROR: Invalid parameters.');

    $hash_length = strlen(hash($algorithm, "", true));
    $block_count = ceil($key_length / $hash_length);

    $output = "";
    for($i = 1; $i <= $block_count; $i++) {
        // $i encoded as 4 bytes, big endian.
        $last = $salt . pack("N", $i);
        // first iteration
        $last = $xorsum = hash_hmac($algorithm, $last, $password, true);
        // perform the other $count - 1 iterations
        for ($j = 1; $j < $count; $j++) {
            $xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
        }
        $output .= $xorsum;
    }

    if($raw_output)
        return substr($output, 0, $key_length);
    else
        return bin2hex(substr($output, 0, $key_length));
}
?>

你给我们解决方案没有使用没有使用
r
rmeador

我通常使用带有用户 ID(或其他一些用户特定信息)的 SHA1 和 salt,有时我还使用常量 salt(所以我有 2 个部分的 salt)。

SHA1 现在也被认为有些妥协,但程度远低于 MD5。通过使用盐(任何盐),您可以防止使用通用 rainbow table 来攻击您的哈希(有些人甚至通过搜索哈希成功地将 Google 用作一种彩虹表)。可以想象,攻击者可以使用您的 salt 生成彩虹表,因此您应该包含用户特定的 salt。这样,他们将不得不为系统中的每条记录生成一个彩虹表,而不仅仅是为整个系统生成一个彩虹表!使用这种加盐方式,即使是 MD5 也是相当安全的。


恒定盐不是一个好主意……可能不是致命的缺陷,但它不必要地削弱了该计划。
MD5 和 SHA1 很快,所以这是一个糟糕的死机。
H
Henrik Paul

在可预见的未来,SHA1 和盐应该就足够了(当然,这取决于您是为 Fort Knox 编写代码还是为购物清单编写登录系统)。如果 SHA1 对您来说不够好,请使用 SHA256

可以说,盐的想法是使散列结果失去平衡。例如,已知空字符串的 MD5 散列是 d41d8cd98f00b204e9800998ecf8427e。因此,如果有足够好的记忆力的人会看到该散列并知道它是空字符串的散列。但是,如果字符串是加盐的(例如,使用字符串“MY_PERSONAL_SALT”),则“空字符串”(即“MY_PERSONAL_SALT”)的散列变为 aeac2612626724592271634fb14d3ea6,因此回溯不明显。我想说的是,最好使用 any 盐,而不是不使用。因此,知道使用哪种盐并不重要。

实际上有 websites that do just this - 你可以给它一个(md5)散列,它会吐出一个已知的明文来生成那个特定的散列。如果您可以访问存储普通 md5-hash 的数据库,那么将管理员的哈希输入到此类服务并登录将是微不足道的。但是,如果密码被加盐,这样的服务将变成无效。

此外,双散列通常被认为是不好的方法,因为它减少了结果空间。所有流行的哈希都是固定长度的。因此,您只能有这个固定长度的有限值,并且结果变得更少变化。这可以被视为另一种形式的盐渍,但我不推荐它。


目标站点不应该包含任何太敏感的东西(它不是银行),但我仍然希望它得到保护。
双散列不会减少结果空间。迭代散列是针对字典和蛮力攻击的常见控制(它减慢它们的速度比它减慢密码检查的速度要大得多)。
@frankodwyer:是的,这很糟糕。 sha1(sha1($foo)) 有效地减少了输出空间,因为任何内部函数的碰撞都会自动变成外部函数的碰撞。退化是线性的,但它仍然是一个问题。迭代散列方法在每一轮中反馈数据,例如 $hash = sha1(sha1($salt . $password) . $salt)。但这仍然不好……坚持使用 PBKDF2 或 Bcrypt……
s
shalvasoft

好的,我们需要盐 盐必须是唯一的,所以让我们生成它

   /**
     * Generating string
     * @param $size
     * @return string
     */
    function Uniwur_string($size){
        $text = md5(uniqid(rand(), TRUE));
        RETURN substr($text, 0, $size);
    }

我们还需要我使用 sha512 的哈希,它是最好的,它在 php 中

   /**
     * Hashing string
     * @param $string
     * @return string
     */
    function hash($string){
        return hash('sha512', $string);
    }

所以现在我们可以使用这个函数来生成安全密码

// generating unique password
$password = Uniwur_string(20); // or you can add manual password
// generating 32 character salt
$salt = Uniwur_string(32);
// now we can manipulate this informations

// hashin salt for safe
$hash_salt = hash($salt);
// hashing password
$hash_psw = hash($password.$hash_salt);

现在我们需要在数据库中保存我们的 $hash_psw 变量值和 $salt 变量

并且为了授权,我们将使用相同的步骤......

这是保护我们客户密码的最佳方式...

Ps 对于最后两个步骤,您可以使用自己的算法...但请确保您可以在将来需要授权用户时生成此哈希密码...


这个问题是关于密码的哈希值。 sha512 的 1 次执行(即使加盐)被广泛认为不足以保护密码。 (另外,RNG 不是加密安全的,因此使用它来生成密码是有风险的)。
你不知道你在做什么。阅读这篇文章中的热门答案,您就会明白为什么您的代码不仅不安全,而且毫无意义。
好的。我的代码不安全。所以让我知道你为什么在你的算法中只使用 sha256 ???我知道sha512是最好的为什么不使用它???
@shalvasoft sha512 非常适合通用散列,但密码保护需要具有非常特定属性的散列(例如,“慢”是一件奇怪的好事,而 sha512 相当快)。有些人使用 sha512 作为构建块来创建密码哈希函数,但现在推荐的方法是“使用 bcrypt 并密切关注 scrypt”。