1231

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

这个问题,“双重哈希”密码是否比仅哈希一次更安全? 建议多次散列可能是一个好主意,而如何为单个文件实施密码保护?建议使用盐。

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

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

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

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

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


PHP简单密码加密中SHA和MD5有什么区别
存储密钥的安全方法,asp.net的密码
如何在Tomcat 5.5中实现加盐密码

4

14 回答 14

1013

免责声明:这个答案写于 2008 年。

从那时起,PHP 就给了我们password_hashpassword_verify而且,自从他们推出以来,它们就是推荐的密码散列和检查方法。

答案的理论仍然是一个很好的阅读。

TL;博士

不要做

  • 不要限制用户可以输入哪些字符作为密码。只有白痴才会这样做。
  • 不要限制密码的长度。如果您的用户想要一个带有 supercalifragilisticexpialidocious 的句子,请不要阻止他们使用它。
  • 不要去除或转义密码中的 HTML 和特殊字符。
  • 切勿以纯文本形式存储用户密码。
  • 永远不要通过电子邮件向您的用户发送密码,除非他们丢失了密码,并且您发送了一个临时密码。
  • 永远不要以任何方式记录密码。
  • 切勿使用SHA1或 MD5 甚至 SHA256 散列密码!现代破解器可以超过 60 和 1800 亿哈希/秒(分别)。
  • 不要将bcrypt 和hash()的原始输出混合使用,要么使用十六进制输出,要么使用 base64_encode。(这适用于任何可能包含恶意\0的输入,这会严重削弱安全性。)

多斯

  • 尽可能使用 scrypt;bcrypt 如果你不能。
  • 如果您不能使用 bcrypt 或 scrypt 以及 SHA2 哈希,请使用 PBKDF2。
  • 当数据库被破坏时重置每个人的密码。
  • 实现一个合理的 8-10 个字符的最小长度,加上至少需要 1 个大写字母、1 个小写字母、一个数字和一个符号。这将提高密码的熵,从而使其更难破解。(有关辩论,请参阅“什么是好的密码?”部分。)

为什么要散列密码呢?

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

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

Whitehat Security 的 CTO Jeremiah Grossman在最近一次密码恢复需要暴力破解他的密码保护后在 White Hat Security 博客上表示:

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

(强调我的。)

什么才是好的密码?

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

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

密码熵很容易近似。使用全范围的 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。

我强烈建议您阅读crypt 函数的文档,如果您想了解如何使用 bcrypt,或者为自己找到一个好的 包装器或使用PHPASS 之类的东西来实现更传统的实现。我建议至少 12 轮 bcrypt,如果不是 15 到 18 轮。

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

平均做法

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

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

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

未来实践

PHP 5.5 中出现了一个完整的密码保护库,它消除了使用 bcrypt 的任何痛苦。虽然我们大多数人在最常见的环境(尤其是共享主机)中都坚持使用 PHP 5.2 和 5.3,但@ircmaxell 已经为即将到来的 API 构建了一个兼容层,该层向后兼容 PHP 5.3.7。

密码学回顾和免责声明

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

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

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

于 2008-12-30T22:15:09.027 回答
145

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

  • PHP 5.5 或更高版本:password_hash()质量很好,是 PHP 核心的一部分。
  • PHP 4.x(过时):OpenWall 的phpass库比大多数自定义代码要好得多 - 用于 WordPress、Drupal 等。

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

快速自测:什么是密码拉伸以及您应该使用多少次迭代?如果您不知道答案,您应该使用password_hash(),因为密码拉伸现在是密码机制的一个关键特性,因为 CPU 速度更快,并且使用GPU 和 FPGA以每秒数十亿次猜测的速度破解密码(使用 GPU )。

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

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

这一切都是因为 Windows仍然 不会加盐或延长其密码,即使在 Windows 10中也是如此。在 2021 年仍然如此。不要犯和微软一样的错误!

也可以看看:

  • 很好的答案,更多关于为什么password_hash()或是phpass最好的方法。
  • 不错的博客文章,为主要算法(包括 bcrypt、scrypt 和 PBKDF2)提供了推荐的“工作因素”(迭代次数)。
于 2011-11-08T12:02:52.677 回答
45

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

于 2008-12-30T22:18:10.237 回答
44

自 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()从中提取。

于 2014-09-21T21:33:41.317 回答
33

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

更多解释可在 - 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 亿)次迭代。密码散列存储在系统 X 上,用于散列的盐值存储在系统 Y 上。这些盐值是可猜测的或已知的(例如用户名) 场景#2:密码散列存储在系统 X 上,用于散列的盐值存储在系统 X 上系统 Y。这些盐值是随机的。如果系统 X 被破坏,你可以猜到,在单独的系统上使用随机盐有一个巨大的优势(场景 #2)。攻击者需要猜测附加值才能破解哈希。如果使用 32 位盐,则猜测的每个密码可能需要 2^32= 4,294,967,296(约 42 亿)次迭代。密码散列存储在系统 X 上,用于散列的盐值存储在系统 Y 上。这些盐值是可猜测的或已知的(例如用户名) 场景#2:密码散列存储在系统 X 上,用于散列的盐值存储在系统 X 上系统 Y。这些盐值是随机的。如果系统 X 被破坏,你可以猜到,在单独的系统上使用随机盐有一个巨大的优势(场景 #2)。攻击者需要猜测附加值才能破解哈希。如果使用 32 位盐,则猜测的每个密码可能需要 2^32= 4,294,967,296(约 42 亿)次迭代。这些盐值是随机的。如果系统 X 被破坏,你可以猜到,在单独的系统上使用随机盐有一个巨大的优势(场景 #2)。攻击者需要猜测附加值才能破解哈希。如果使用 32 位盐,则猜测的每个密码可能需要 2^32= 4,294,967,296(约 42 亿)次迭代。这些盐值是随机的。如果系统 X 被破坏,你可以猜到,在单独的系统上使用随机盐有一个巨大的优势(场景 #2)。攻击者需要猜测附加值才能破解哈希。如果使用 32 位盐,则猜测的每个密码可能需要 2^32= 4,294,967,296(约 42 亿)次迭代。

于 2010-02-12T00:52:13.353 回答
29

我只想指出 PHP 5.5 包含一个密码哈希 API,它提供了一个crypt(). 此 API 显着简化了散列、验证和重新散列密码散列的任务。作者还发布了一个兼容性包(以单个 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 的回答中简要提到了这一点。我将把这个答案留在这里,因为我认为它提供了更多关于它如何工作以及它为那些不了解安全性的人提供的易用性的信息。

于 2013-04-27T21:41:15.257 回答
21

我正在使用Phpass,它是一个简单的单文件 PHP 类,几乎可以在每个 PHP 项目中轻松实现。另请参阅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);
于 2012-06-26T08:05:12.333 回答
15

要记住的事情

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

服务器

端口

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

用户名

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

密码

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

数据库

服务器

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

用户

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

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

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

密码

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

PHP

密码

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

哈希

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

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

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

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

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

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

用户创建密码

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

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

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

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

http://git.io/mSJqpw

于 2014-04-09T15:01:02.887 回答
12

谷歌表示 SHA256 可用于 PHP。

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

于 2008-12-30T22:20:54.337 回答
8

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

于 2008-12-30T22:29:28.640 回答
8

我在这里找到了关于这个问题的完美话题: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));
}
?>
于 2013-09-19T13:26:29.167 回答
7

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

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

于 2008-12-30T22:18:25.993 回答
5

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

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

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

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

于 2008-12-30T22:21:57.767 回答
-7

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

   /**
     * 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 对于最后两个步骤,您可以使用自己的算法...但请确保您可以在将来需要授权用户时生成此哈希密码...

于 2015-10-08T04:05:05.313 回答