Salting hashes sounds like one of the steps of a hash browns recipe, but in cryptography, the expression refers to adding random data to the input of a hash function to guarantee a unique output, the hash, even when the inputs are the same. Consequently, the unique hash produced by adding the salt can protect us against different attack vectors, such as rainbow table attacks, while slowing down dictionary and brute-force attacks.
Note: Never tell anyone using your registration forms that their selected password is not unique. A system like that in place will allow hackers to crack passwords in record time!
Hashed passwords are not unique to themselves due to the deterministic nature of hash function: when given the same input, the same output is always produced. If Alice and Bob both choose
dontpwnme4 as a password, their hash would be the same:
As we can see,
bob have the same password as we can see that both share the same hash:
The attacker can better predict the password that legitimate maps to that hash. Once the password is known, the same password can be used to access all the accounts that use that hash.
Can you find what is
jason's password based on the hash
Attacking Unsalted Passwords
To start, the attacker could try a dictionary attack. Using a pre-arranged listing of words, such as the entries from the English dictionary, with their computed hash, the attacker easily compares the hashes from a stolen passwords table with every hash on the list. If a match is found, the password then can be deduced.
Two different hash functions can produce the same hash; however, the risk of this happening is extremely low. But, how do attackers know which hash function to use? It's not too hard.
Fortunately, despite choosing the same password,
bob chose a password that is not easily found in a dictionary:
dontpwnme4. Our friend
mike, on the other hand, chose
friendship as his password which is a direct entry in the English dictionary.
mike is at high risk of being breached through a dictionary attack; the risk for
bob is no different. To come up with a password such as
dontpwnme4, the attacker could use special dictionaries such as leetspeak to crack the password.
Both dictionary attacks and brute-force attacks require the real-time computation of the hash. Since a good password hash function is slow, this would take a lot of time. To circumvent this problem, the attacker may rely on a rainbow table.
A rainbow table can make the exploitation of unsalted passwords easier. A rainbow table is essentially a pre-computed database of hashes. Dictionaries and random strings are run through a selected hash function and the input/hash mapping is stored in a table. The attacker can then simply do a password reverse lookup by using the hashes from a stolen password database.
The main difference between a rainbow table attack and a dictionary and brute-force attack is pre-computation. Rainbow table attacks are fast because the attacker doesn't have to spend any time computing any hashes. The trade-off for the speed gained is the immense amount of space required to host a rainbow table. We could say that a rainbow table attack is a pre-computed dictionary and/or brute-force attack.
Since time and space are limited, the attacker that designs and computes the rainbow table may want to process the most commonly used passwords first. Here is where
bob could be at a much higher risk if
dontpwnme4 is in that common-password list. Large common-password databases are created using frequency analysis across passwords collected from different publicly leaked breaches.
The strength of rainbow tables comes from volume not computation speed and the volume is huge! Each data breach adds to this volume. For a list of companies that have been breached visit the pwned websites list of haveibeenpwned.com.
"There are often 'breaches' announced by attackers which in turn are exposed as hoaxes. There is a balance between making data searchable early and performing sufficient due diligence to establish the legitimacy of the breach." - Troy Hunt
Faster CPUs and GPUs, distributed computations and weak algorithms are making cracking a password much easier. To mitigate this trend, the security industry has created passwordless authentication as an alternative.
Mitigating Password Attacks with Salt
To mitigate the damage that a rainbow table or a dictionary attack could do, we salt the passwords. According to OWASP Guideliness, a salt is a fixed-length cryptographically-strong random value that is added to the input of hash functions to create unique hashes for every input, regardless of the input not being unique. A salt makes a hash function look non-deterministic, which is good as we don't want to reveal password duplications through our hashing.
Let’s say that we have password
farm1990M0O and the salt
f1nd1ngn3m0. We can salt that password by either appending or prepending the salt to it. For example:
f1nd1ngn3m0farm1990M0O are valid salted passwords. Once the salt is added, we can then hash it. Let's see this in action:
Prepending the Salt
Appending the Salt
The hashes were calculated using the following Python code:
import hashlib string = "saltedpassword" hashlib.sha256(string.encode()).hexdigest()
This demonstrates the importance of using unique salts. Let’s say that we decide to always append the salt to the passwords. If two users use the same password, we are just creating longer passwords that won’t be unique in our database. Both salted passwords would hash to the same value. But, if we choose another salt for the same password, we get two unique and longer passwords that hash to a different value. Let's visualize this through an example:
Alice and Bob decide to use both the same password,
farm1990M0O. For Alice, we'll use
f1nd1ngn3m0 again as the salt. However, for Bob, we'll use
f1nd1ngd0ry as the salt:
Hashing and Salting Alice's Password
Hashing and Salting Bob's Password
Different users, same password. Different salts, different hashes. If someone looked at the full list of password hashes, no one would be able to tell that Alice and Bob both use the same password. Each unique salt extends the password
farm1990M0O and transforms it into a unique password.
In practice, we store the salt in cleartext along with the hash in our database. We would store the salt
f1nd1ngn3m0, the hash
07dbb6e6832da0841dd79701200e4b179f1a94a7b3dd26f612817f3c03117434, and the username together so that when the user logs in, we can lookup the username, append the salt to the provided password, hash it, and then verify if the stored hash matches the computed hash.
Now we can see why it is very important that each input is salted with unique random data. When the salt is unique for each hash, we inconvenience the attacker by now having to compute a rainbow table for each user hash. This creates a big bottleneck for the attacker. Ideally, we want the salt to be truly random and unpredictable to bring the attacker to a halt.
While the attacker may be able to crack one password, cracking all passwords will be unfeasible. Regardless, when a company experiences a data breach, it is impossible to determine which passwords could have been cracked and therefore all passwords must be considered compromised. A request to all users to change their password should be issued by the company right away.
"If someone breaches into a company database, the company must react as if all passwords were cracked, even if hashing the passwords involved using a salt."
Generating a Good Random Salt
f1nd1ngn3m0 a good salt? When we are adding salts to passwords, we need to add salts that are cryptographically strong and credential-specific.
Following OWASP Guidelines, to properly implement credential-specific salts, we must:
Generate a unique salt upon creation of each stored credential (not just per user or system-wide).
A system-wide salt is pointless to mitigate attacks; it would just make passwords longer. Additionally, we don't want to implement user-based salts because we want to hash and salt each password created for a user. That includes passwords created during registration or as the result of a password reset. If the user eventually cycles over the same password, we don't want to give away that the password has already been used.
Use cryptographically-strong random data.
Cryptographically strong or strong cryptography define a cryptographic system that is highly resistant to cryptoanalysis, which are efforts to crack down the secret patterns of the system to breach it. Showing that a cryptographic scheme is resistant to attacks is a complex process that requires a lot of time, extensive testing and reviews, and ideally community engagement. Due to this complexity, security experts recommend that you don't roll your own cryptography.
To create such cryptographically-strong random data, we may use a cryptographically secure pseudorandom number generator(CSPRNG) to gather unpredictable input from sources that we cannot observe, such as the Random Generator API of our operating system. Even better, we could use a battle-tested, cryptographic library for that. Most of these libraries include facilities for working with random numbers. As an advice, never roll your own random number generators.
OWASP suggests SecureRandom as an example of cryptographically-strong random data.
As storage permits, use a 32-byte or 64-byte salt (actual size dependent on protection function).
A longer salt effectively increases the computational complexity of attacking passwords which in turn increases the candidate set exponentially. A longer salt also increases the space required to store rainbow tables while decreasing the possibility that such table exists in the wild.
Scheme security does not depend on hiding, splitting, or otherwise obscuring the salt.
Simply put, do not mess with the salt. The salt doesn't need to be encrypted, for example. Salts are in place to prevent someone from cracking passwords at large and can be stored in cleartext in our database next to the hashes. However, we do not want to make the salts readily accessible to the public. For that reason, usernames are bad candidates to use as salts.
"Hashing salts are speed bumps in an attacker's road to breaching your data. It does not matter if they are visible and unencrypted, what matters is that they are in place."
Based on these guidelines,
f1nd1ngn3m0 is not being generated from an unpredictable source. In fact, the salt is a
1337 way of writing
findingnemo, a popular animated movie, which could be part of a dictionary-brute-force strategy.
f1nd1ngn3m0 doesn't meet the length recommendation to be a salt: it's only 11 bytes long.
Our second salt,
f1nd1ngd0ry suffers from the same weaknesses. I chose it based on it being the sequence to the "Finding Nemo" movie, "Finding Dory". Our human imagination to create randomness can only go so far so it's better to delegate that task to the machine.
How do we generate reliable random data to serve as salt?
As we can see, hashing and salting are very complex processes and the security of our systems greatly relies on their successful implementation. While these are no methods to create 100% secure systems, these are methods to create hardy and resilient systems. It's best to leave the creation, maintenance, and operation of such methods and systems to security experts. A misstep in your home-made security strategy may lead to extensive damage to your business, users, and reputation.
"Hashing and salting are complex methods to create hardy and resilient systems. It's best to leave their implementation to security experts. A misstep in a home-made security strategy may lead to damage to a business, its users, and reputation."
You'd want to rely on algorithms such as
bcrypt that hash and salt the password for you using strong cryptography. Additionally, you may use a security framework, such as Spring Security for the Java Ecosystem for example. These frameworks offer you abstractions that make the development of your applications safer but also integrate with reliable identity providers, such as Auth0, that make Identity and Access Management much easier.
- A cryptographic salt is made up of random bits added to each password instance before its hashing.
- Salts create unique passwords even in the instance of two users choosing the same passwords.
- Salts help us mitigate rainbow table attacks by forcing attackers to re-compute them using the salts.
- Creating cryptographically strong random data to use as salts is very complex and it's a job better left to leading security solutions and providers.
Simplifying Password Management with Auth0
You can minimize the overhead of hashing, salting and password management through Auth0. We solve the most complex identity use cases with an extensible and easy to integrate platform that secures billions of logins every month.
Auth0 helps you prevent critical identity data from falling into the wrong hands. We never store passwords in cleartext. Passwords are always hashed and salted using bcrypt. Additionally, data at rest and in motion is always encrypted by using TLS with at least 128-bit AES encryption. We've built state-of-the-art security into our product, to protect your business and your users.
Make the internet safer, sign up for a free Auth0 account today.