openssl_digest vs hash vs hash_hmac?SALT 和 SALT 的区别HMAC?

2021-12-27 00:00:00 hash passwords security php

我想使用 SHA512 来存储密码.为此,我应该使用 openssl_digesthashhash_hmac 中的哪一个,为什么?

I want to use SHA512 to store passwords. To do that, which of openssl_digest, hash and hash_hmac should I use and why?

SALT 和有什么区别?HMAC?

我刚刚读到 HMAC 是建立在哈希函数之上的.

I just read that HMAC is built on top of hash function.

那么SHA512+SALT+HMAC真的有必要还是SHA512+SALTSHA512+HMAC?

So is SHA512+SALT+HMAC really necessary or SHA512+SALT or SHA512+HMAC?

推荐答案

所以,首先,让我们弄清楚一件事.openssl_digest() === hash().它只是另一个名称不同的函数,它执行完全相同的操作.它计算输入的加密哈希.

So, first off, let's clear one thing up. openssl_digest() === hash(). It's just another function by a different name that does the exact same thing. It computes a cryptographic hash of the input.

那么,现在我们有一个问题:存储密码时,哪个更好:hash 还是 hash_hmac?

So, now we have the question: When storing passwords, which is better: hash or hash_hmac?

都没有

事实证明,彩虹桌已死.仅使用 hash($password . $salt) 甚至 hash_hmac($password, $salt) 都不足以存储密码.时期.如果您正在这样做,请立即停止.

As it turns out, The Rainbow Table Is Dead. Just using hash($password . $salt) or even hash_hmac($password, $salt) is not good enough for password storage. Period. If you're doing so, stop right now.

原因很简单:计算机(或 GPU)上的计算时间非常便宜.它太便宜了,以致于对密码列表进行暴力破解已经足够便宜,您需要担心它.请记住,哈希函数旨在快速.不贵...

The reason is simple: computation time on a computer (or GPU) is incredibly cheap. It's so cheap, that to brute force a list of passwords is cheap enough that you need to worry about it. Remember, hash functions are designed to be fast. Not expensive...

但是,事实证明,有一种方法可以使那些快速散列函数更加昂贵.事实上,它非常简单:迭代.

But, as it also turns out, there is a way to make those fast hash functions more expensive. In fact, it's pretty simple: iterate.

现在,我知道你在想什么.您将只循环遍历哈希:

Now, I know what you're thinking. You're going to just loop over the hash:

function hash_password($password, $salt) {
    $hash = hash("sha512", $password . $salt);
    for ($i = 0; $i < 1000; $i++) {
        $hash = hash("sha512", $hash);
    }
}

当然够好了,对吧?不.正如散列和加密之间的根本区别中所述,这不是一个好主意.那么为什么不直接反馈密码并再次加盐呢?

Surely that's good enough, right? Nope. As explained in Fundamental Difference Between Hashing and Encryption, that's not a good idea. So why not just feed back the password and salt in again?

function hash_password($password, $salt) {
    $hash = hash("md5", $salt . $password);
    for ($i = 0; $i < 1000; $i++) {
        $hash = hash("md5", $hash . $password);
    }
}

事实上,这正是 PHPASS 使用的(略有调整,但这是基本算法)...

In fact, this is exactly what PHPASS uses (slightly tweaked, but this is the base algorithm)...

所以现在对 hash_password 的 1 次调用会执行 1000 个哈希周期.

So now 1 call to hash_password executes 1000 hash cycles.

但是我们可以改进吗?

好吧,事实证明,我们可以.下一个合乎逻辑的事情是看看我们是否可以在相同的时间内获得更多的哈希周期.这就是 hash_hmac() 的用武之地.事实证明,HMAC 每次被调用时使用 2 个哈希周期.而且因为它都是 C 语言,所以它只需要 hash() 完成一轮所需的时间的 1.5 倍左右.

Well, as it turns out, we can. The next logical thing to do would be to see if we can get more hash cycles for the same amount of time. And this is where hash_hmac() comes in. As it turns out, HMAC uses 2 hash cycles each time it's called. And because it's all C, it only takes about 1.5 times the amount of time that hash() takes to do a single round.

这意味着如果我们将 hash 替换为 hash_hmac,我们可以立即看到在指定时间内完成的工作量增加了 33%.所以现在我们在这里:

So that means if we replace hash with hash_hmac, we can instantly see a 33% increase in the amount of work being done in a specified time. So now we're here:

function hash_password($password, $salt) {
    $hash = hash_hmac("md5", $salt, $password);
    for ($i = 0; $i < 1000; $i++) {
        $hash = hash_hmac("md5", $hash, $password);
    }
}

这实际上是PBKDF2的基本内循环.

And this is actually the basic inner-loop of PBKDF2.

但我们能变得更好吗?

是的,我们可以变得更好.如果我们仔细观察,我们会发现——除了密码和盐之外——上述所有算法都使用了非常少的内存.在 sha512 的情况下,他们将使用大约 128 到 256 个字节(缓冲区和状态)来散列密码.由于内存使用量非常小,因此在 GPU 中同时并排运行很多内存是微不足道的.如果我们只能增加内存使用...

Yes, again, we can get better. If we look closely, we can see that -in addition to password and salt- all of the above algorithms use a very small amount of memory. In the case of sha512, they'll use on the order of 128 to 256 bytes (buffers and state) to hash the password. Since the memory use is so small, it's trivial to run a lot of them at once side-by-side in a GPU. If we could only increase the memory usage...

好吧,事实证明,我们可以简单地使用 bcrypt,这是一种自适应散列算法.它的优势在于它比上述算法使用更多的内存(大约 4 到 5kb).所以它更难并行化.由于计算成本高昂,因此它可以抵抗暴力破解.

Well, as it turns out, we can simply use bcrypt, which is an adaptive hashing algorithm. It has an advantage that it uses more memory than the above algorithms (on the order of 4 to 5kb). So it's more resistent to parallelizing. And it's resistent to brute forcing since it's computationally expensive.

幸运的是,它可用于 PHP:

Luckily, it's available for PHP:

crypt($password, '$2y$07$usesomesillystringforsalt$')

注意 crypt() 使用了很多算法,但是 $2y$$2a$ 算法是 bcrypt.

Note that crypt() uses many algorithms, but the $2y$ and $2a$ algorithms are bcrypt.

但是我们可以改进吗?

有点.有一种相对较新的算法,称为 scrypt.它比 bcrypt 更好,因为它在计算上同样昂贵,但使用了更多的内存(大约 20mb 到 40mb 来散列单个密码).因此,它更难并行化...

Kind-of. There is a relatively new algorithm called scrypt. It's better than bcrypt, because it's just as computationally expensive, but uses a LOT more memory (on the order of 20mb to 40mb to hash a single password). Therefore, it's even more resistent to parallelization...

不幸的是,scrypt 在 PHP 中 不可用(我正在努力改变它).在那之前,使用 bcrypt...

Unfortunately, scrypt is not available in PHP yet (I'm working on changing that). Until then, use bcrypt...

最近从 LinkedIn 吸取教训后,LastFM、Hotmail,Gawker 等,证明很多的人都做错了.不要做错,使用带有经过审查的算法的库.使用CRYPT_BLOWFISH (bcrypt),使用PHPASS,使用PasswordLib.但是不要仅仅因为你不想拉依赖关系就发明你自己的......那只是疏忽.

After the recent lessons from LinkedIn, LastFM, Hotmail, Gawker, etc, the proof is apparent that a lot of people are doing it wrong. Don't do it wrong, use a library with a vetted algorithm. Use CRYPT_BLOWFISH (bcrypt), use PHPASS, use PasswordLib. But don't invent your own just because you don't want to pull a dependency... That's just negligence.

更多阅读:

  • 正确加盐密码 - 反对 Pepper 的案例
  • GPU 加速 PBKDF2
  • 多次哈希迭代,每次都追加盐?
  • MD5 解码,他们是怎么做的

相关文章