openssl_digest vs hash vs hash_hmac? SALT和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?

What is the difference between SALT & HMAC?

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

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


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.

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

Short Answer:


Long Answer:

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.

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);

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

So now 1 call to hash_password executes 1000 hash cycles.

But can we improve on that?

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.

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);

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

But can we get better?

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...

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.

Luckily, it's available for PHP:

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

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

But can we improve on this?

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...

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


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.

More reading:

dongming0505 我和你在一起需要一个合适的算法,并且在你的答案中一般写在stackoverflow上。 然而,彩虹表并没有死,也没有过时。 它们仍然是一个非常有效和强大的工具。
接近 8 年之前 回复
dsnnvpobaljihv3490 除了你错过了那篇文章的全部内容(和这个答案)。 不是不需要盐。 这是他们还不够。 sha512($ password。$ salt)用于所有实际目的,与sha512($ password)一样糟糕。 您需要使用适当的密码散列算法,该算法设计得很慢(并且所有这些算法都设计为使用盐)。 这就是帖子的重点。 即使哈希表类比不完整,它仍然100%有效...
接近 8 年之前 回复
dongyou6909 您关联的博客文章“彩虹表已经死了”是基于对它们如何工作的错误假设; 它们不是哈希表,可以在@security.stackexchange.com找到一个很好的解释。 由于现代密码散列算法设计缓慢且存储容量缓慢,因此时间存储传输很快就会倾向于预先计算攻击。 盐对于它们来说仍然是最好的,也是非常需要的。
接近 8 年之前 回复
dpxo13079 +1很棒的答案
大约 8 年之前 回复

According to IT Security experts:

Use Bcrypt Source:

I would give answer according to SO point of view.

openssl_digest vs hash vs hash_hmac
  1. openssl_digest - Computes a digest.
  2. hash Generate a hash value (message digest)
  3. hash_hmac — Generate a keyed hash value using the HMAC method

And In cryptography, a hash-based message authentication code (HMAC) is a specific construction for calculating a message authentication code (MAC) involving a cryptographic hash function in combination with a secret key.

As said by ircmaxell, hash or hash_hmac are not better for storing passwords with SHA-512. I would rather say, you can use openssl_digest for storing passwords.

See SHA-512 library for PHP

  1. A hash, in this context, is a one-way function - i.e. a function that makes it very easy to find the result from the argument (the password) but difficult (or impossible) to find any argument that generates a given result.
  2. A salt is some auxiliary data that augments the argument to a hash function. This is useful as it prevents accidental discovery of passwords through observation that two hashed passwords have identical values. With a salt, the stored/transmitted value will only be identical if both the salt and the password match.
  3. An HMAC refers to the application of a hash (and optional salt) to a "message authentication code" - which, depending upon context might be a password... or, at least, there's nothing stopping you passing a password into the HMAC as if it were the message authentication code.

HMAC is meant to be used in cases where you have a random and secret key. For these cases, HMAC is usually better than other ways of incorporating the key into the hash function. (For example, using HMAC takes care of things like extension attacks, etc.)

Salt is usually a random value that is not secret. That is to say, when you use the term salt you usually refer to situations where there is a random value that may very well be known to the attacker. The security of the system should therefore not depend on the salt being kept secret. In these situations HMAC is often not a very good choice.

HMAC and Salt comparison is not logical. Personally I'd use a salt and a hash function... and I wouldn't be paranoid about the strength of the hash function as its unlikely to be the weak link in any practical system....


donglanfu5831 这就是我说使用Bcrypt的原因。
大约 8 年之前 回复
dongqiao2077 两点:首先,如果hmac不是盐的好选择,为什么NIST批准的PBKDF2算法会将其用于此目的? 其次,openssl_digest与hash()完全相同。 它计算加密哈希(也称为摘要)。 实际上,它使用与hash()相同的算法。 因此,如果hash()不好(正如你所指出的那样),openssl_digest()也没有...另外,HMAC被证明不会削弱原始哈希,哈希(密码+盐)不是......
大约 8 年之前 回复

HMAC is a specific way to use a hash algorithm (like SHA512). It's used to sign a message and you can then verify that the message is from a specific signer and has not been altered. So this isn't what you want.

A salt is used to add a bit of "randomness" to a text that should be encrypted or hashed. The point is that even if you encrypt the same text several times you'd get different results. This makes it harder to do some attacks. This is what you want: SHA512(salt+password).

For storing passwords, the most secure way I could imagine would be:

(disclaimer: I'm not very experienced with cryptography and there might be a better solution)

  • Client (JavaScript code?) would generate a salt value.
  • The client then combines salt and password, and run the result through your hashing algorithm.
  • The client then transmits both salt and hash value to the server which stores it (preferably in different locations).

To verify a password, you'd then do:

  • Pass the salt to the client.
  • Client combines salt and entered password, runs it through your hashing algorithm.
  • Client sends the hash value to the server.
  • Server compares the hash value with the stored hash value. If they match, it was the same password.

Of course you could transmit the password in plaintext and do the whole salting and hashing on the server, but this would weaken your solution dramatically. You should never transmit the password in plaintext.

But the "pass the salt to the client" part might be a problem. One way that I could imagine to solve this would be to somehow derive the salt from the username (easiest way: simply do lowercase(username) + password), but the problem with that would be that the salt would be predictable and thus weakening your solution a little bit. Yet, it's still way better than transmitting the "raw" hash and you wouldn't even need to store the salt as you could derive it from the username every time. Should your password DB get stolen it would still resist a rainbow table attack with this "salting with username" approach.

The problem is that a man-in-the-middle attack is still possible. If an attacker would intercept username and hash it has all the relevant infos and it wouldn't be any different than transmitting the plaintext password. So you might want to secure the connection with SSL (HTTPS).

dongxian0421 嗯...给它一些想法。
8 年多之前 回复
dquh37673 你是对的,在服务器上生成salt可能会更好。 但是在客户端生成不会使彩虹表攻击成功,因为它们只是因为存在盐而被击败。
8 年多之前 回复
drrhr20884 谢谢你的回答。 不会在客户端产生盐攻击(彩虹攻击)? 在服务器上生成它不是更好吗? 可能使用https进行传输?
8 年多之前 回复
Csdn user default icon