2

Many questions have been asked about hashing passwords in (web) applications, but I'm experiencing a different issue. I know that the application I'm currently working on isn't doing it safely enough (just sha1 without salts or anything), but it's kinda hard to change it all of a sudden.

I will have to find a way to implement a new algorithm for all (± 50.000) users. I've been thinking about a few solutions, but none of them sound right.

1) Adding a second column to the user table with the new password. Every time the user gets authenticated, I store the password with its new hash and throw away the old one. This will in practice mean that it'll take years before the sha1 phases out.

1.1) Do the above but stimulate the users to log back in to our system for a security update, but that really feels as admitting a (pre)consisting security vulnerability. That's not gonna be to my management's liking.

2) Re-authenticate all users and throw away all passwords. This is very rigorous, and also a pain in the behind for the users.

How would you cope with this problem?

Community
  • 1
  • 1
Sherlock
  • 7,525
  • 6
  • 38
  • 79

3 Answers3

3

You could extend your existing solution by using the sha1 hashes as input to your new improved hash implementation, where you can add the salt etc. The new solution would then include the sha1 step + whatever improvements you might want.

This way you can calculate the new hases from the hashes you already have, but at the same time improve the solution.

Ebbe M. Pedersen
  • 7,250
  • 3
  • 27
  • 47
  • 1
    I don't know why I haven't thought of that, but that's really smart. That saves loads of troubles. Thanks! – Sherlock Jul 31 '14 at 22:49
  • But it still leaves you with goofy legacy crud in your hashing algorithm. I'd suggest doing this as a transtional measure for the existing hashes, but moving to a cleaner algorithm for all *new* hashes, so that you can eventually drop the legacy one. – Wyzard Jul 31 '14 at 22:51
  • As I understand it, you are recommending storing pbkdf2( salt, sha1(passwd)) in the database and then zapping the sha1 column. I think this is a good idea. In fact, in view of the heartbleed vulnerability, one could argue that user passwords should never be handled directly on server side, and instead there should be client side processing. Doing the sha1 on the client side would be a step in the right direction (better if we could do everything on client side but that is a bit more complex than what I can explain here). – TheGreatContini Aug 01 '14 at 21:25
2

I'd do it the way you suggested first: whenever the user authenticates, store the new hash and discard the old. You don't necessarily have to put the new hash in a separate database column; you could instead add a column that just says which format the hash is in, and use the same column for the actual hash regardless of its format.

You don't have to force users to log in just to have their password hashes updated, but there might be some other event in the future (such as an unrelated security flaw) that provides a better reason to reset passwords or otherwise make everyone log in.

After a year, anyone who still has their password hash in the old format is someone who hasn't logged in for a year. It might be a good time to send them a reminder email and eventually delete the unused account.

Wyzard
  • 33,849
  • 3
  • 67
  • 87
1

Every password-storing-system must have the option to switch to a better hash algorithm, your problem is not a one-time migration problem. Good password hash algorithms like BCrypt or PBKDF2 have a cost factor, from time to time you have to increase this cost factor (because of faster hardware), then you need the exact same procedure as you need for the migration.

Today's password libraries will often generate a string containing all parameters like salt and cost factor. This format is generated for exactly the reason that you can switch to a safer algorithm without losing existing passwords. The verifying procedure knows what algorithm to use for the verification, so you can see that this is indeed an "official" solution.

$2y$10$nOUIs5kJ7naTuTFkBy1veuK0kSxUFXfuaOKdOKf9xYT0KKIGSJwFa
 |  |  |                     |
 |  |  |                     hash-value = K0kSxUFXfuaOKdOKf9xYT0KKIGSJwFa
 |  |  |
 |  |  salt = nOUIs5kJ7naTuTFkBy1veu
 |  |
 |  cost-factor = 10 = 2^10 iterations
 |
 hash-algorithm = 2y = BCrypt

Normally it's not necessary to reset the passwords, one can just wait until the user logs in the next time, then you have the plaintext password and can calculate a more safe hash. If the existing hashes are very weak, as in your case with an unsalted SHA-1, then you can give more protection immediately with hashing the old hash, this is especially easy with unsalted hashes.

$hashToStoreInDb = new_hash($weakUnsaltedHashFromDb);

For verification you can then:

  1. First try to verify the entered password with the new algorithm.
  2. If it does not match, compare it with the double hash algorithm new_hash(old_hash($password)).
  3. Should the double hash value match, then you can calculate and store the updated hash.

Make sure that you first check for the newest algorithm, and afterwards for older algorithms. Then the login will take longer only the next time the user logs in, and new users are not affected by backwards compatibility issues. Of course you could also make use of the format above, to mark it as double hash.

Peter O.
  • 32,158
  • 14
  • 82
  • 96
martinstoeckli
  • 23,430
  • 6
  • 56
  • 87