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. The unique hash produced by adding salt protects against various attack vectors, such as hash table attacks, while significantly slowing down dictionary and brute-force offline attacks.
However, it's crucial to understand the limitations of salt protection. If an attacker launches a credential-stuffing attack against an online service (a subset of brute force attacks), salts won't help because the legitimate server performs the salting and hashing operations.
Would you like to watch the video version of this article?
Security Tip: Never inform users through registration forms that their selected password isn't unique. Such a system would enable attackers to systematically enumerate user passwords, significantly weakening password security and making the system vulnerable to a plethora of attacks.
Hashed passwords, due to the deterministic nature of hash functions, aren't naturally unique: the same input always produces the same output. If Alice and Bob both choose
dontpwnme4
as a password, their unsalted hashes would be identical:username | hash (SHA-256) |
---|---|
alice | 4420d1918bbcf7686defdf9560bb5087d20076de5f77b7cb4c3b40bf46ec428b |
jason | 695ddccd984217fe8d79858dc485b67d66489145afa78e8b27c1451b27cc7a2b |
mario | cd5cb49b8b62fb8dca38ff2503798eae71bfb87b0ce3210cf0acac43a3f2883c |
teresa | 73fb51a0c9be7d988355706b18374e775b18707a8a03f7a61198eefc64b409e8 |
bob | 4420d1918bbcf7686defdf9560bb5087d20076de5f77b7cb4c3b40bf46ec428b |
mike | 77b177de23f81d37b5b4495046b227befa4546db63cfe6fe541fc4c3cd216eb9 |
As demonstrated,
alice
and bob
share the same hash value (4420d1918bbcf7686defdf9560bb5087d20076de5f77b7cb4c3b40bf46ec428b
), indicating they've chosen identical passwords. This pattern creates serious security vulnerabilities. An attacker can better predict the password that legitimately maps to that hash. Once the password is known, the same password can be used to access all the accounts that use that hash. According to the 2023 SpyCloud Identity Exposure Report, 72% of users in 2022 reuse previously exposed passwords across accounts, making hash uniqueness crucial for security.Challenge: Can you determine
's password from the hashjason
? Probably not. However, modern GPUs can compute billions of hashes per second, making traditional unsalted hashes susceptible to high-speed cracking attempts.695ddccd984217fe8d79858dc485b67d66489145afa78e8b27c1451b27cc7a2b
When attackers obtain a database and discover duplicate hashes, they can quickly deduce that either no salts are being used or the system employs weak hashing algorithms. A high frequency of identical hashes often indicates the presence of default passwords. These scenarios typically involve offline attacks against exfiltrated data, where attackers can leverage massive computational resources to crack passwords at unprecedented speeds.
The modern threat landscape, particularly with the rise of specialized hardware and cloud-based cracking services, makes proper salt implementation more crucial than ever.
Attacking Unsalted Passwords
Let's explore how attackers exploit unsalted password hashes. The first line of attack is typically a dictionary attack. An attacker uses a pre-computed list of common words and their hashes, such as entries from language dictionaries, known password lists, and common variations. By comparing these pre-computed hashes against stolen password hashes, attackers can quickly identify matches. According to the 2023 SpyCloud Identity Exposure Report, dictionary-based password patterns still appear in over 60% of compromised credentials.
Technical Note: While different hash functions can theoretically produce the same output for different inputs (a collision), the probability is astronomically low for secure algorithms like SHA-256. However, attackers can often identify the hash function used through various analysis techniques.
In our example, Alice and Bob selected
dontpwnme4
, which is not a dictionary word but is still vulnerable. Meanwhile, Mike chose friendship
, which is a direct dictionary entry. While Mike's password is immediately vulnerable to dictionary attacks, Alice and Bob aren't necessarily safer. Modern attack dictionaries include leetspeak variations and common password patterns, making passwords like dontpwnme4
increasingly vulnerable.Dictionary and brute-force attacks require computing hashes in real-time. Since a properly designed password hash function is intentionally slow (a security feature we'll explore later), these attacks can be time-consuming. This leads us to more sophisticated approaches.
Cracking Unsalted Hashes with Pre-computed Tables
Attackers have evolved beyond simple dictionary attacks, employing two powerful tools: hash tables and rainbow tables. Each offers different trade-offs between lookup speed, storage requirements, and computational overhead.
A hash table provides lightning-fast password cracking through pre-computation. Instead of calculating hashes on the fly, attackers create massive databases mapping passwords to their hashes. When they obtain a password database, they can instantly look up the original passwords. If a hash matches one in their table, the corresponding password is immediately revealed. In essence, a hash table attack is a pre-computed dictionary and/or brute-force attack, trading storage space for incredible speed.
The strength of hash tables comes from sheer volume, not computational speed. Each new data breach potentially adds millions of password hashes to these tables. For a sobering look at the scale of this problem, check out the pwned websites list at haveibeenpwned.com, which hints at a growing number of compromised credentials that attackers can leverage for hash tables.
But there's another clever technique in the attacker's arsenal: rainbow tables. While hash tables offer instant lookups at the cost of massive storage requirements, rainbow tables take a more sophisticated approach. They use chains of hashes and special reduction functions to compress what would be terabytes of hash tables into manageable sizes. We can visualize them as a space-saving trick: instead of storing every single hash, rainbow tables store clever "shortcuts" through the hash space. But, as with everything, they come with a trade-off: Looking up a password takes longer because you need to regenerate parts of the chain, but the storage savings can be enormous.
This space-time trade-off makes rainbow tables particularly potent when attacking unsalted hashes. While they might take longer than hash tables to crack a single password, their smaller size means attackers can carry vast password databases in much less space. When combined with modern hardware acceleration, rainbow tables remain a formidable tool for password cracking. The "Have I Been Pwned" database contains millions of unique password hashes from real-world breaches, illustrating the scale of available pre-computed data.
This is particularly dangerous for Alice and Bob if their chosen pattern (
dontpwnme4
) appears in common password lists. Modern attackers build these lists through sophisticated frequency analysis of leaked passwords. The NIST Password Guidelines specifically recommend checking passwords against known breach datasets for this reason.Industry Insight: "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
The threat landscape continues to evolve. With modern hardware acceleration (GPUs, FPGAs), distributed cracking networks, and increasingly sophisticated algorithms, password cracking becomes more efficient every year. However, credential stuffing remains more prevalent than hash cracking, highlighting the importance of Multi-Factor Authentication (MFA) as an additional security layer.
Mitigating Password Attacks with Salt
To mitigate the damage that a hash table or a dictionary attack could do, we salt the passwords. According to OWASP Guidelines, a salt is a value generated by a cryptographically secure function. When added to the input of hash functions, a salt creates unique hashes for every input, regardless of the input not being unique. Salt provides the hashing operation with non-deterministic behavior as it prevents revealing duplicate passwords through a hashing mechanism.
Consider a practical example with the password
farm1990M0O
and salt f1nd1ngn3m0
. We can implement salting by either appending or prepending the salt: farm1990M0Of1nd1ngn3m0
or f1nd1ngn3m0farm1990M0O
. Once the salt is added, we can then hash it. Let's examine both approaches:Prepending the Salt
Password:
farm1990M0O
Salt:
f1nd1ngn3m0
Salted input:
f1nd1ngn3m0farm1990M0O
Hash (SHA-256):
7528ed35c6ebf7e4661a02fd98ab88d92ccf4e48a4b27338fcc194b90ae8855c
Appending the Salt
Password:
farm1990M0O
Salt:
f1nd1ngn3m0
Salted input:
farm1990M0Of1nd1ngn3m0
Hash (SHA-256):
07dbb6e6832da0841dd79701200e4b179f1a94a7b3dd26f612817f3c03117434
The hashes were calculated using the following Python code:
import hashlib string = "saltedpassword" hashlib.sha256(string.encode()).hexdigest()
This implementation demonstrates why unique salts are critical. If we consistently append the same salt to passwords, identical passwords would still produce identical hashes in our database. However, by using different salts, we create unique combinations that hash to distinct values.
Let's examine this through another example where Alice and Bob choose 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
User: Alice
Password:
farm1990M0O
Salt:
f1nd1ngn3m0
Salted input:
farm1990M0Of1nd1ngn3m0
Hash (SHA-256):
07dbb6e6832da0841dd79701200e4b179f1a94a7b3dd26f612817f3c03117434
Hashing and Salting Bob's Password
User: Bob
Password:
farm1990M0O
Salt:
f1nd1ngd0ry
Salted input:
farm1990M0Of1nd1ngd0ry
Hash (SHA-256):
11c150eb6c1b776f390be60a0a5933a2a2f8c0a0ce766ed92fea5bfd9313c8f6
This example illustrates how different users with identical passwords generate distinct hashes through unique salts. Each unique salt effectively transforms the base password
farm1990M0O
into a unique password. Importantly, when users update their passwords, the system should generate new salts.In production systems, we store three elements: the salt in cleartext, the resulting hash, and the username. During login, we: 1. Retrieve the stored salt for the username 2. Append it to the provided password 3. Generate a hash 4. Compare with the stored hash value
The uniqueness of salt per hash creates a significant computational barrier for attackers, who must now generate separate hash tables for each user. For optimal security, salts should be truly random and unpredictable.
While this approach significantly increases security, we must maintain perspective: when a breach occurs, all passwords should be treated as potentially compromised, regardless of salting implementation. Organizations should immediately: 1. Notify users 2. Require password changes 3. Generate new salts during the reset process
“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.”
Tweet This
Generating a Good Random Salt
Is
f1nd1ngn3m0
a good salt? When we are adding salts to passwords, we need to add salts that are cryptographically strong and credential-specific. Let's explore what makes a salt truly effective for password security.Following OWASP Guidelines, to properly implement credential-specific salts, we must understand several key principles:
A system-wide salt is pointless to mitigate attacks; it would just make passwords longer. Such an approach easily allows an attacker to keep using hash tables. Instead, we should hash and salt each password created for a user. This means generating a unique salt upon creation of each stored credential (not just per user or system-wide). This includes passwords created during registration or as the result of a password reset. Even if the user eventually cycles over the same password, we don't want to give away the fact that the password has already been used.
Cryptographically strong or strong cryptography defines a cryptographic system that is highly resistant to cryptanalysis, which are efforts to decipher the secret patterns of a system. Showing that a cryptographic scheme is resistant to attacks is a complex process that requires extensive time, testing, reviews, and community engagement. Due to this complexity, security experts strongly 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 should use a battle-tested cryptographic library for this purpose. Most of these libraries include robust facilities for working with random numbers. Remember: never roll your own random number generators.
OWASP suggests SecureRandom as an example of cryptographically-strong random data generation.
As storage permits, use a 32-byte or 64-byte salt, with the actual size dependent on the 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 hash tables while decreasing the possibility that such a table exists in the wild.
The security of this scheme does not depend on hiding, splitting, or otherwise obscuring the salt. Simply put, do not mess with the salt. For example, the salt doesn't need to be encrypted. Salts are in place to prevent someone from cracking passwords at large and can be stored in cleartext in the database. However, do not 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.”
Tweet This
Let's analyze our previous examples: Is
f1nd1ngn3m0
a good salt? Not really. It's not being generated from an unpredictable source. In fact, it's a 1337
way of writing findingnemo
, a popular animated movie, which could be part of a dictionary-brute-force strategy. Moreover, at only 11 bytes long, it fails to meet the length recommendation for a secure salt.Our second salt,
f1nd1ngd0ry
, suffers from the same weaknesses. Chosen based on the sequel to "Finding Nemo", it demonstrates why human imagination for creating randomness can only go so far. It's better to delegate that task to the machine.As we can see, hashing and salting are complex processes, and our systems' security greatly relies on their successful implementation. While there are no methods to create 100% secure systems, these are methods to create hardy and resilient ones. It's best to leave the creation, maintenance, and operation of such methods and systems to security experts. A misstep in your homemade 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 homemade security strategy may lead to damage to a business, its users, and reputation.”
Tweet This
You'd want to rely on algorithms such as
bcrypt
that hash and salt the password for you using strong cryptography. Additionally, consider using a security framework, such as Spring Security for the Java ecosystem. These frameworks offer abstractions that make your applications safer while also integrating with reliable identity providers, such as Auth0, that make Identity and Access Management much easier.Recap
- A cryptographic salt is made up of random bits added to each password instance before its hashing.
- Salts create unique hashes even when two users choose the same passwords.
- Salts help us mitigate hash table attacks by forcing attackers to re-compute them using the salts for each user.
- 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.
About the author
Dan Arias
Staff Developer Advocate
The majority of my engineering work revolves around AWS, React, and Node, but my research and content development involves a wide range of topics such as Golang, performance, and cryptography. Additionally, I am one of the core maintainers of this blog. Running a blog at scale with over 600,000 unique visitors per month is quite challenging!
I was an Auth0 customer before I became an employee, and I've always loved how much easier it is to implement authentication with Auth0. Curious to try it out? Sign up for a free account ⚡️.View profile