Passwords are fun and sweet. They keep our domains protected.
Most applications store our passwords hashed with a cryptographically secure hash as an additional "security" layer.
A hashed password makes it so that in an event a database is compromised (OH NO!) the evil doers won't get their hands on the plain text passwords.
Which is nice!
There is however one slight small problem.
A simple hash, even with a random salt is considered weak. How come?
Bad Guys Setup TM
Modern bad guys might use a setup of eight (8x) Nvidia GTX 1080 graphic cards. Each card has 2560 cores running at 1.6 GHZ. That’s 20.480 cores used for brute force computations. Meaning that for a simple SHA512 hash they’d be able to do 8 BILLION computations per second.
Note that I said considered, we'll get to discover some alternatives.
Summary at the end of the article
Why is the current password hashing endless?
We won't go in details of how and why certain hashing technologies are bad or broken.
Nowadays it;s pretty easy to use lookup tables, rainbow tables, reverse lookup tables. GPU's to reverse plain dictionary passwords with computations and even the all time bruteforcing combinations.
Simple passwords are easy to break, no matter what type of hashing you use. Even with 74 possible combinations per character, a 7 character password hash would be cracked in 25 minutes.
So the focus has been in the last years to ensure that the computational process increase to protect our passwords.
Using techniques like key stretching. Making the hashing slow and tedious.
This means that we're increasing the complexities of hashing as to do even 100.000 rounds of hashing as in PBKDF2.
Or you can specify a certain computational overhead in terms of CPU or memory as in Argon2 which will make it slower.
But at what cost?
At development implementation cost. Time spent devising new technologies. Implementing them. Adopting them. Arguing about best practices etc. Researching. You get the point.. All while developer debt increases and we haven’t really secured our systems in the first place!
Some people might think that the computational overhead needs to be considered.
And indeed it DOES need to be considered. As complex schemes have to be devised to avoid getting a denial of service from too many computations.
Again, lets' be honest, even if a big application requires thousands of people to log in every second it's not likely that this will be a real bottleneck. Unless someone decides to do a brute force via proxies… then you get a beautiful DDOS!
What's the real problem of password hashing complexities ?
Our bad guys might have 20.000 cores at their disposition. But our VPS’es and servers only have on average 2 to 4 cores! 0.5 of a second may be too much to and won’t make much of a difference in the end.
This consideration becomes apparent and in itself is a small logical fallacy. There are many explanations to why this is.
In this article I'll show certain methods of thinking which makes the complex process irrelevant and a simple HMAC-SHA-512 will suffice!
Working our way to the right solution
We’ll work our way up to the best solution in a few steps. At the same time this will help us secure our application even more.
Step 1 - Strong Long Passwords
If a password is weak it's going to be broken anyway no matter which complexity an application implements to try to keep your data secure.
A secure passwords itself is going to provide you with all the security you need.
I know that not everyone uses randomly generated passwords with high entropy for each website.
There's no excuse from not being able to use KeePass or derivates like lastpass to store your passwords.
Heck, even your browser can store them!
Have a minimum password length of let's say 16 characters. Maybe even making it 20, 24. With enough entropy the hashing issues disappear.
You don't need to enforce all the complexities of passwords which exist like using X special characters and blablabla.
All you need is the length of the password.
If it's a sentence with a combination of a number,uppercase and any special character (non alphanumeric) then it's perfect.
Even if you'd have a 24 character password which is made a weird looking sentence and only a uppercase letter and 2 numbers it's extremely safe.
Let’s presume a 16 characters long password where you only use lowercase alphanumeric letters and numbers. This will ensure that 36 possibilities exist per character. We’ll go wild and envision a high end hacker which can generate 1 TERA hashes per second. That will lead to 25.000 years.
As long as your users are not using “Passw0rdPassw0rdPassw0rd” you’re all safe.
This in itself works best with step 2.
“But I don’t want to stress my users”
A common objection might be that you don’t want to stress your users to remember long passwords etc.
Two distinct solution exists for that common fallacy!
You can - but shouldn’t really – allow 8+ character passwords. Whenever a password is lower than 14 characters you can do one of the following:
Use argon2, bcrypt, or PBKDF2 for the all the passwords under 14 characters, the normal hashing for those above.
Encrypt the password using the generated HMAC-SHA256.
Key = HMAC-SHA256(unique_salt, pepper <> password)
password_hash = HMAC-SHA516(unique_salt, pepper <> password)
stored_password = AES-GCM-256(key, password_hash)
Verification? Decrypt the stored hashed password from the database and verify if it’s equal to the password_hash with the password provided by the user. Whenever someone puts their hands on our database they’ll have a very hard time because the AES-GCM encrypted version doesn’t say too much. The only way to reverse it would be to constantly generate the password key and then try to decrypt it.
Even in case of a collission of sha256 you’d still have to verify that the HMAC-SHA-516 is the same for the password as it can be a lucky coincidence!
Solution 2 - Per password dynamic salt
Another solution would be to generate a cryptographically secure strong random string of 32,64, 128 bytes to use as a salt on a per user/password basis.
This way the salt will be UNIQUE for each password. On a password change generate a NEW salt.
This can be stored either in the database or somewhere else.
I'm sure that some "experts" might object that this isn't secure because if your database is whacked then your passwords can be quickly reversed.
This might be true but if someone gets to your database then it's over anyway because they might have even more important data. Even access to your full server.
So using of a salt and unique pepper, or another random crypto which is stored somewhere else will ensure another safety margin.
So the next step is to implement is encryption on another very valuable point of data.
Solution 3 - Secrets, Keys and Encryption
With the advent of GDPR systems might need to become more secure to adhere to the GDPR!
I use *might* because it depends on the company doing the implementation. Psuedo anonymisation might be enough.
However I'd propose that by using a clever encryption strategy you'll secure your system in ways which will make the password hashing complexities obsolete.
Let's do an imaginary thought exercise together.
Let's say that ONLY your database has been compromised. Now the attacker has access to your hashed.. it doesn't matter how easy or difficult it is to crack them. Let's say they're all doable in reasonable time.
We're considering that the address email is the username. Now if the email/username where to be hashed and encrypted (2 in 2 or 2 separate fields) then the attacker would have a bad time.
Even if they'd manage to reverse engineer the password they'd need to reverse engineer the e-mail and/or username.
Which would add to the complexity of everything!
The complete solution in action
Enforce a minimum length for passwords, 16 characters is fine, the more the better.
Generate a unique random cryptographic secure salt which is unique per user/password combination. You can encrypt it for additional “security”. You can store it in a different table or in the same table.
Have a strong unique pepper, another per application unique cryptographic salt you’ll store somewhere safe and use in conjunction with your unique salt.
Hash the e-mail in the database with a application wide unique pepper/salt. This will be used for fast lookups. Since e-mails are pretty long/complex and might take a long time to brute force. (Average e-mail address length is 21.9 characters long) https://www.freshaddress.com/blog/long-email-addresses/
Encrypt the e-mail so you have reversed access to it when needed.
Last but not least, yes, you will be able to use a simple HMAC-SHA512 with a single iteration. You can add more if you want.
Since you’ve abstracted away the password/email combination it doesn’t matter that the bad guys can reverse the password faster than usual, because they’ll need to reverse the e-mail as well in order to be able to do anything. And since we’ve used unique salts and peppers it’s going to take some time.
This is what I've implemented for my Burebista CMS solution this website is running on now.
Subscribe to my newsletter