5
$\begingroup$

Background [Updated]
Consider a situation where a website was created over a decade ago and user account password storage was done by storing SHA1(password) into the database.

It would be great to upgrade the system to use Pbkdf2(salt, password) with a large work factor of say 50000 for the password hashing using HMACSHA256 as the prf. The resultant hash | salt could be stored in the database and used to validate the visitor when they return and supply a password. This approach seems to be well regarded here on crypto.stackoverflow.com.

It would be great to be able to improved the hashing for all the existing accounts without needing users to reset their password. So I'm wondering if the following approach is good from a security perspective.

  OldHash = SHA1(password)
  UpgradedHash = Pbkdf2(salt , OldHash )

  StoredInDatabase = UpgradedHash | salt

One desirable property of this approach is that all existing password hashes can be upgraded to using Pbkdf2, salt and a high work factor without needing to have the passwords reset. All the new passwords could even be handled the same way just to keep all the hashes consistent. So new passwords could work like this:

  Hash = Pbkdf2(salt , SHA1(password))
  StoredInDatabase = Hash | salt

Rational for Upgrading the Hashes
The need to upgrade the password hashes is because the SHA-1 hashes don't include salt and SHA-1 has since been reported as broken. The goal in upgrading the password hashes in the database is to reduce the likelihood that a hacker with access to the database of password hashes could obtain the corresponding passwords for those accounts.

Question
Does this seem like a security valid approach for upgrading the password hashing approach in an existing system?

$\endgroup$
  • 1
    $\begingroup$ Yes, hashing the old hash again is a valid approach, but why are you considering PBKDF2 here and not something more modern like bcrypt or Argon2? $\endgroup$ – SEJPM Mar 14 '17 at 19:49
  • $\begingroup$ Good question, I know bcrypt is popular here and requires more memory which can slow down a brute force attack. But Pbkdf2 is still pretty modern (has a work factor built in) and it's available as a standard method in .net (the platform the system is on). Bcrypt and Argon2 are not. That was really the reason. $\endgroup$ – Ron C Mar 14 '17 at 20:01
  • $\begingroup$ Check your API, PBKDF2 almost certainly has two inputs not one. $\endgroup$ – otus Mar 14 '17 at 20:13
  • 1
    $\begingroup$ Straight iteration based PBKDFs (like PBKDF2) should no longer be considered modern. You can get a massive speed-up with them on massively parallel hardware with little memory (bandwith). Bcrypt on a GPU as about as fast as on a CPU, similar with Argon2 but even better (especially with resistance to ASIC optimizations). $\endgroup$ – SEJPM Mar 14 '17 at 20:19
  • $\begingroup$ @otus - It's true, my PBKDF2 library has multiple parameters. In my original post I showed that I was passing in salt concatenated to the old hash, but actually the salt is passed in as a separate parameter to the PBKDF2 function. For those interested this is the actual method signigure public static byte[] Pbkdf2(string password, byte[] salt, KeyDerivationPrf prf, int iterationCount, int numBytesRequested) The method is part of this assembly: Assembly Microsoft.AspNetCore.Cryptography.KeyDerivation I will update my question to show salt as a parameter rather than a concatenation. $\endgroup$ – Ron C Mar 15 '17 at 12:05
3
$\begingroup$

Yes, mostly

But wait, isn't SHA-1 broken?

It is. SHA-1 has collisions found as of 2017. But this isn't important for our usage, since we only require that SHA-1 won't collide for our inputs. Difference is that with 2017 attack attacker knows plaintext and tries to find another plaintext that gives same hash, in our case attacker doesn't know anything (not even hash). And we can assume that SHA-1 won't collide for real-life passwords, since if this was case it would be very major breakage of SHA-1 (that would have been found earlier).

There is however one catch: SHA-1 will limit your security to 80bits, since it has 160bit output and birthday attack can "limit security" to half of output bits (in our case). This is in fact reason PKDBF1 was abandoned in favor of PKDBF2, but most passwords are far from 80bits in security.

Note: I didn't take any of other problems into account in my answer. Choice of PKDBF2 might be considered outdated by this point too. I personally would suggest Argon2 (but some would call it too new to be adopted). Also PKDBF2 doesn't have one input, as others noted, but I'll assume it's just overlook.

$\endgroup$
  • $\begingroup$ The thinking behind upgrading the password hashes in the database was because the original SHA-1 hashes didn't include salt and SHA-1 has since been reported has broken. The goal in upgrading the password hashes in the database is to reduce the likelihood that a hacker with access to the database of password hashes could obtain the corresponding passwords for those accounts. I've updated my question to reflect this. $\endgroup$ – Ron C Mar 15 '17 at 12:16
  • $\begingroup$ @RonC yes I understand. But SHA-1 being broken is not affecting your scheme (it was just as terrible as before) which was explained in my answer. Moreover, chaining SHA-1 with PKDBF2 doesn't make it more secure in case of collision! But since you deal with passwords, this is irrelevant here. If you want to decrease likelihood of attacker getting user passwords, preferably use something better than PKDBF2 (but even PKDBF2 > SHA-1 by far). $\endgroup$ – axapaxa Mar 15 '17 at 18:10
  • $\begingroup$ So are you saying that Pbkdf2( salt , password) produces a hash that is harder to brute force find a password for than Pbkdf2( salt , sha1(password)) because using sha1 limits the security to 80 bits (given it's 160 bits of output)? Or are you saying that PKDBF2 with a 256 bit psudo random salt isn't a sufficiently good approach to password hashing in your opinion? $\endgroup$ – Ron C Mar 15 '17 at 18:22
  • $\begingroup$ Sorry, correction, the PKDBF2 salt is a 128 bit psudo random number. $\endgroup$ – Ron C Mar 15 '17 at 18:28
  • 1
    $\begingroup$ Both. Sha1 limits your security to 80bit (which is enough IMO), and IMO PKDBF2 is poor choice for KDF in 2017. Size or presence of salt has nothing to do with any of these (but of course you should use salt, and 128bit is more than enough). $\endgroup$ – axapaxa Mar 15 '17 at 23:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.