Hash the hash - Password hashing migration

Posted on 7. November 2021

The number of data leaks including hashed passwords have increased from year to year and with the rising number of web applications, they will continue this path. In these dumps, lots of passwords of older accounts were hashed by the insecure SHA1 or even the absolutely unsafe MD5 algorithm. Data security doesn't get the love it should get. Time to act.

What the hash?

Passwords were usually not saved as plain text in the database so nobody - including the administrator - can see them. Instead, they were saved running through a cryptographic hash function which maps the password to a fixed size so called hash value. This hash value is practically infeasible to reverse - well only practically. There exist multiple attack scenarios against older hash functions which makes them now completely unsafe.


A collision attack in which two different inputs result in the same hash value exists for example for SHA1.

Still a lot of people were using the same password over and over. A leaked password in SHA1 besides their email address can result in a terrible outcome. People can log into different services and can order something, damage a reputation or something even worse.

As a user, always use a different password for each service to protect yourself!

Migration example

One of the latest bigger breaches was the Twitch.tv leak with over 125GB of compressed data including the source code and other data. By looking at the source code, we can learn how they have handled their password security.


Twitch.tv is using bcrypt for password hashing - but only for accounts which have a PasswordCreatedOn value. For older accounts, they are hashing the provided password with SHA1 including a salt first before they are validating this hash with bcrypt against the saved hash in the database.


Hash the Hash

Twitch.tv has done good job in its password hashing migration hashing the old hash with a newer, much safer algorithm so all the passwords were at least hashed with the newer hash. By this way, no SHA1 passwords were left in the database.

For this migration, the old hashes have to be migrated with a task runner first to the new hashing algorithm which is very simple. When a user wants to log in then, the password needs to be validated either by the migration or the new default hashing algorithm.

Example code in PHP for validation and creation of the new hash

/** * Verifies the given password with the migrated hash * * @param $password * @param $salt * @param $savedHash * * @return bool */ function verifyPassword($password, $salt, $savedHash) { $oldHash = sha1($salt.$password); return password_verify($oldHash, $savedHash); } /** * Creates a password hash with the current default hashing algorithm. * * @param $savedHash * * @return string|false|null Returns the hashed password, or FALSE on failure, or null if the algorithm is invalid */ function rehash($savedHash) { return password_hash($savedHash, PASSWORD_DEFAULT); }

Tip: In PHP, you can use the constant PASSWORD_DEFAULT as second parameter for password_hash which will change in newer PHP releases when newer, stronger hashing algorithms are supported. The function password_verify will automatically choose the correct algorithm to verify the password.

Made with ♥️ and Gatsby © 2022