I want to hash passwords before storing them to the database. There are many samples out there on how to hash passwords, the following C# code from the docs relies on the HMACSHA1 algorithm:
public static void Main(string[] args)
{
Console.Write("Enter a password: ");
string password = Console.ReadLine();
// generate a 128-bit salt using a secure PRNG
byte[] salt = new byte[128 / 8];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(salt);
}
Console.WriteLine($"Salt: {Convert.ToBase64String(salt)}");
// derive a 256-bit subkey (use HMACSHA1 with 10,000 iterations)
string hashed = Convert.ToBase64String(KeyDerivation.Pbkdf2(
password: password,
salt: salt,
prf: KeyDerivationPrf.HMACSHA1,
iterationCount: 10000,
numBytesRequested: 256 / 8));
Console.WriteLine($"Hashed: {hashed}");
}
I would like to know if there is a way to determine the length of the password hash based on the length of the password. So if the user has a password with a length of x is there a way to calculate the length of the hash?
I want to know it because in my database the password column currently is a varchar taking 128 characters. The REST API built on top of it should restrict the password length so that the database will never crash because the password is too long and generates a password hash longer than 128 characters.
Or is it best practise to say the database column is a varchar of 256 characters and the API only allows passwords smaller or equal than 30 characters so it will never hit the limit?
It would be nice if the answer is independent from the code language, this is more a question in general.
The output of PBKDF2 can be specified. A PBKDF is a password based key derivation function. Generally those have a key expansion phase that allows the output to be specified.
However, if PBKDF2 is used as password hash rather than for key derivation the size of the configured hash is kept; that provides the maximum security that can be retrieved from the algorithm. In this case that's SHA-1 that generates 160 bits / 20 bytes.
Unless you really need text, the output can be stored as static binary of 20 bytes. In your case you should be storing it as base 64 version of the 20 bytes. That should amount to a fixed 28 bytes: ((20 + 2) / 3) * 4 = 28
to calculate the base 64 expansion. However, your code explicitly specifies the output size to be 256 / 8 = 64
bytes. A quick calculation suggests that it always uses 88 base 64 characters for that size.
Producing 64 bytes while using SHA-1 is not a good setting because it requires the inner function of PBKDF2 to run 4 times, giving you no advantage of running it only once to produce 20 bytes, giving advantage to an attacker. An attacker only has to check the first 20 bytes to make sure a password matches, after all, and for that only one of the four runs is required. The method that PBKDF2 uses to expand the key size over the hash size is really inefficient and may be considered a design flaw.
On the other hand, 10.000 iterations is not very high. You should, for PBKDF2:
The size of the password doesn't have any influence on the size of the password hash.
Beware that some password hashes on other runtimes create a password hash string themselves, more compatible with crypt
on Unix systems. So they would have a larger output that is not directly compatible.