【问题标题】:Password hashing methods密码哈希方法
【发布时间】:2023-06-01 09:15:02
【问题描述】:

我正在创建一个基于 PHP 的 CMS(使用 MVC 架构)。我想散列存储在数据库中的用户密码。我已经阅读了很多关于这个主题的文章和教程,但我遇到了反对意见/观点和建议。我有点困惑。我正在寻找实现密码散列的最佳方法。这些是我遇到的概念/方法:

首先,许多人混淆了单向散列和两向加密。如果我没记错的话,2路加密是关于使用公私钥对的不对称加密的事情,它是为了保护数据并使其只有知道另一个密钥(密钥)的人才能读取。所以这是我们现在不关心的。

PHP 提供了许多函数来制作哈希,其中一些直接对给定数据使用哈希算法(md5()sha1()sha256()ripemd160() 等)其中一些接受给定数据并支持的算法并生成哈希(hash()hash_init()hash_hmac() 等) 散列/加密方法的负载有什么区别? (hash_pbkdf2()crypt()bcrpytpassword_hash() 和上面提到的其他人) 据我所知,在散列中使用盐是一种好习惯,但多次散列是一个坏主意(即使有很多人认为它是好的)。有些函数使用盐,有些函数使用密钥...

问题 1: 有人能澄清一下例如:md5('myPassword');hash('md5', 'myPassword'); 之间的区别吗(我知道 md5 是一种易于破解的方法,不建议用于存储密码)

问题2: keysalt 有什么区别?那么hash_hmac('sha1', 'myPassword', 'HaCK_MeIF_youCAN');sha1('myPassword'.'HaCK_MeIF_youCAN'); 有什么区别呢? (注意hash_hamc 调用它的第三个参数'key')

问题 3: 多重哈希真的是一种不好的做法吗? 喜欢:

hash = sha512(password)

for i in range(10000):
    hash = sha512(hash) + salt

return hash

问题 4: 散列密码的最佳方法应该是什么?

由于这是一个重要且敏感的问题,我认为像我这样不熟悉该主题的其他人希望将这个哈希问题弄清楚,并希望一劳永逸地获得准确可靠的答案,我请您回答如果您是 IT 安全专家,或者您拥有该主题的任何证书或学位! (对于在互联网上获得知识的自称安全专家:您使用散列密码完成数百个系统/网站的事实并不意味着它们是安全的!)

最后一个要求: 简单的(初学者)网络程序员既不是外星人,也不是理论数学家。所以请尝试用一些类似人类的英语来解释:)

【问题讨论】:

标签: php security encryption hash


【解决方案1】:

已经有很多关于 Stack Exchange 上的密码哈希的问题,所以这基本上是一个重复的问题,但由于你不知道该相信什么,我会给你一些指示。

回复:问题 1:可能它们只是相同代码的不同接口。 (不过我还没有检查。如果你想确保在两者中输入相同的输入并比较输出。或者只是查看文档或源代码。)

回复:问题 2:密钥是您通常不应该公开的私人信息(非对称加密中的 公钥 除外)。通常在密码散列中没有密钥。盐是一条随机的公共信息,旨在将您的密码哈希与世界上所有其他预先计算的哈希表区分开来。是对rainbow tables的主要防御。

HMAC(基本上m,kHash(k||Hash(k||m)) 用于连接操作||)被设计为所谓的message authentication code。但是,它(ab)用于许多其他目的,因为如果使用良好的加密哈希函数进行实例化,它的多功能设计。在这种情况下,在 HMAC 中使用 salt 作为密钥,使用密码作为消息与 Hash(password||salt) 并没有什么不同。

回复:问题 3:不,这是一个很好的做法。密码存储的主要攻击场景是您的数据库遭到破坏(如果您的密码数据足够重要以至于人们会关心,通常会发布所有密码数据)。大多数非技术人员(显然甚至是一些具有 IT 背景的人员)经常重复使用他们的密码(即使每个人都会得到他们的密码哈希海峡)。因此,如果您将密码保存为明文,那么随着您的数据库泄露,很多人突然知道登录信息。为了防止这种情况,您希望对密码使用单向函数。不幸的是,大多数时候密码的熵很少(因为没有人喜欢记住密码安全的密码),因此如果您使用快速哈希函数,您可以尝试每个可能的密码并将哈希值与数据库中的密码进行比较。举个极端的例子,假设我只使用密码catdog,并且你有我的密码的sha1 哈希值:8f6abfbac8c81b55f9005f7ec09e32d29e40eb40echo $password | sha1sum 生成。读者练习:使用了哪个密码?

要缓解此问题,您需要使用慢速散列函数,以便尝试每个可能的密码会花费大量时间。减慢散列的一种方法是迭代散列几千(甚至几十万)次。不过,还有其他慢速散列概念,例如 bcrypt,它们不只是迭代散列。

回复:问题 4:可以在 security.stackexchange.com:https://security.stackexchange.com/questions/211/how-to-securely-hash-passwords 上找到深入讨论。 tl,dr 版本是 CodeInChaos 的评论:只需使用 password_hash,它大多是万无一失的。可以在此处找到除盐之外的胡椒高级主题资源:https://security.stackexchange.com/a/3289/10727

【讨论】:

    【解决方案2】:

    我将尝试回答您的一些问题,但首先要谈谈加密。加密的问题是它是双向的,知道密钥就可以找回密码。仅存储哈希(单向)将更好地保护密码,因为即使所有代码(包括密钥)都已知,也无法检索密码。

    问题 1 和 2: 避免使用所有快速散列算法很重要,这包括 MD5、SHA*。因为它们是如此之快,所以它们很容易被暴力破解。它是例如可以使用普通硬件每秒计算大约8 Giga MD5 哈希值。

    问题 2: 应该为每个密码随机生成一个盐,但它不是秘密的,可以与密码一起明文存储在数据库中。密钥(有时称为胡椒)应保密,不存储在数据库中。

    问题 3: 多重哈希是一件好事,但前提是正确完成。 BCrypt 和 PBKDF2 等适当的算法以安全的方式执行此操作,并且它们具有决定迭代次数的成本因素。这个成本因素可以适应未来更快的硬件。

    问题 4: 目前,您能做的最好的事情是使用 PHP 函数 password_hash() 创建 BCrypt 哈希。早期版本还存在compatibility pack。我写了一篇关于安全存储密码的tutorial,我试图以一种希望可以理解的形式更深入地回答这个问题,所以你可以看看它。

    【讨论】:

    • 因为 ACs 已经很困惑了,所以我会对这些条款挑剔。根据定义,加密总是双向的,没有所谓的“单向加密”。这将被称为单向散列(或单向函数)。很不幸,当很多消息来源真正想说“密码散列”时,却在谈论“密码加密”。
    • @Perseids - 这实际上是我写的,我认为如果明确提及它会更清楚。无论如何,我试着稍微改写一下。
    • 您对本教程的一些反馈感兴趣吗?
    • @Perseids - 当然,我在本教程上工作了很长时间。请记住,英语不是我的母语,但这不得影响内容的正确性。
    • @Perseids - 非常感谢您的反馈,非常感谢!你也有邮件:-)