How to Hash a Long Password with bcrypt

Hashing is an important component in securing any application that users will log into, such as a Web application or an API for an an app, particularly when authentication is done using passwords, and bcrypt is currently a standard, acceptable way to create a secure hash. bcrypt’s acceptability may change in the future, but what won’t change is the reality that no sane person stores user passwords values as clear text in a file or database. Let me say that again. No sane person stores user password values as clear text in a file or database. Instead, the current best practice is to store a one way cryptographic hash based on the password and later, when the user logs in, hash the password that the user supplies with the one previously stored on the system.

Beyond simple hashing of a user supplied password, there are other use cases for wanting to securely hash the entirety of an arbitrarily long string using bcrypt such as constructing authentication tokens. One may also wish to salt the password before passing it into bcrypt for an even higher level of entropy.

One algorithm I use to get around the character limit is to use an intermediate hash. So, instead of hashing the password directly, I use a weaker hashing mechanism that produces a shorter string within the length limitation of bcrypt and then hash that intermediate string using bcrypt. The intermediate hash needn’t be strong hash, like bcrypt, though if you use a highly randomized and long salt you will maximize the effectiveness of this approach. My current intermediate hash is sha512.

A Recipe for bcrypt Hashing Passwords Using and Intermediate Hash

The steps I use for hashing ginormous strings with bcrypt, such as heavily salted passwords, are as follows:

  1. 1. Generate a long, cryptographically secure salt using a source such as /dev/urandom. (Do this per user. You can also append an application-wide salt as well if you like.) You will need to store this salt if you plan to do password validation in the future.
  2. Concatenate the salt to the user supplied password.
  3. Feed the concatenated value to sha512 and capture the result, which will be a 128 character string representing a hexidecimal number.
  4. To add entropy and make use of the entire intermediate salt, loop through the entire sha512 hash taking pairs of hex digits, converting the pair’s value to an ascii character and using each character to build a compacted intermediate hash that is 64 characters long where there are 256 possible characters.
  5. Feed the final compacted intermediate hash into bcrypt with your preferred parameters (such as cost) and capture the resulting hash.
  6. You now have a secure hash that utilizes the entirety of the value of your long password.

This is a simple solution to the problem of limited string length support for the bcrypt hashing algorithm, but there is room for improvement. For instance, using some simple stupid math tricks, one could create variations in the length of the hashed string, which up to a minimum viable length could be used to produce a larger number of possible hash-able values. To the extent that variation and unpredictability continue to make hashed values harder to guess, adding procedural complexity to this solution is really up to the creativity of the individual implementing it.

PHP Sample bcrypt Intermediate Hash Code Example

<?php
/**
 * A simple demonstration impementation in PHP.
 * This code snippet requires PHP version 7.2 or higher. Check
 * out PHP 7.2+'s improved encryption capabilities at
 * https://php.net
 */

// This is the string that needs a one way hash. A clever user
// could make it longer than bcrypt can handle.
$password = 'spicy pony head rules';
echo "The example password is: {$password}\n\n";

// Create hypothetical cryptorgraphically secure random values for
// an application secret and a user secret. Secrets should be quite
// large so they cannot be easily guessed or brute forced.

// The application has a secret stored in a private file. It should
// be randomly generated.
$applicationSecret = base64_encode(random_bytes(2048));
echo 'Application Secret: ' . substr($applicationSecret, 0, 48) . "...\n";

// The user secret is stored with other user data. Each user gets
// their own random secret.
$userSecret = base64_encode(random_bytes(2048));
echo 'User Secret: ' . substr($userSecret, 0, 48) . "...\n";

// If you concatenate these strings, they are too long for bcrypt to use the
// entire string.
$stringToHash = $password . $userSecret . $applicationSecret;
$len = strlen($stringToHash);

echo "The hashable string is {$len} characters long.\n\n";

// So, lets use sha512 as our intermediate hash since it gives us 128 bytes
// of hexidecimal values.
$intermediateHashedString = hash('sha512', $stringToHash);
$len = strlen($intermediateHashedString);
echo "The sha512 string is {$len} characters long. SHA512:\n{$intermediateHashedString}\n\n";

// Now, let's get full use of this string by taking pairs and using
// their numeric value to create a string of one byte characters.
// Effectively, we've created a base 256 modal number that fits
// the entire intermediate hash into bcrypt's input length constraint.
// (Don't output this to your terminal. It's likely full of control
// characters).
$base256HashedString = '';
for ($i = 0; $i < $len; $i += 2) {
    $base256HashedString .= chr(hexdec(substr($intermediateHashedString, $i, 2)));
}

$len = strlen($base256HashedString);
echo "The base 256 compacted sha512 string is {$len} characters long. \n\n";


// Finally, let's serve up our bcrypt hash.
$bcryptHash = password_hash($base256HashedString, PASSWORD_BCRYPT);
echo "Out final bcrypt hash is: {$bcryptHash}\n";

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s