RedTeam Nexus Labs
Module 15 · Lesson 03 // Hashes

salts

Hashes · CTF

Freereading · 10 min
On this page
LESSON

Overview

If two users choose the same password and a system stores an unsalted hash of it, both records hold the identical digest — and an attacker can precompute digests for common passwords once and look them all up instantly. A salt defeats these shortcuts. This lesson explains what a salt is, why it works, and how it changes the economics of attacking stored hashes, all in a defensive, educational frame.

Learning objectives

  • Define a salt and explain how it is used when hashing.
  • Explain how salts defeat rainbow tables and precomputation.
  • Explain how salts prevent identical passwords from producing identical hashes.
  • Describe correct salt handling: unique, random, and stored alongside the hash.

Core concept

A salt is a random value generated for each password and combined with that password before hashing. Instead of storing hash(password), the system stores hash(salt + password) together with the salt itself. The salt is not secret — it is stored right next to the hash — but it must be unique per password and randomly generated. Its job is not to hide anything but to make every stored hash different and unpredictable, even when the underlying passwords are the same.

How it works

Salting attacks two shortcuts that make unsalted hashes cheap to break:

  • Precomputation (rainbow tables). Without salts, an attacker can compute the hashes of millions of common passwords once, store them in a lookup table (a rainbow table is a space-optimized form of this), and then match any stolen unsalted hash instantly. A per-password salt means the attacker would need a separate table for every possible salt value — precomputation becomes useless because the table built for one salt does not apply to any other.

  • Deduplication across users. Without salts, two users with the same password have the same digest, so cracking one reveals the other, and an attacker can see which accounts share a password. A unique salt makes hash(salt_A + "password") and hash(salt_B + "password") completely different, so identical passwords no longer collide in the store and each must be attacked individually.

Because the salt is unique per password, the attacker is forced back to attacking one hash at a time: they can still guess a specific password and hash it with that record's salt, but they lose all the bulk, reuse-based advantages. Salting does not slow down a single guess — that is the job of a slow password-hashing function, covered in the next lesson. Salting removes precomputation and deduplication; slow KDFs raise the cost of each individual guess. The two work together.

Practical example

Two users in an isolated lab both pick the password summer2024:

Unsalted:
  alice -> hash("summer2024") = d7a8...   (same)
  bob   -> hash("summer2024") = d7a8...   (same)  <- identical, and precomputable

Salted (unique random salt per user):
  alice -> salt = "9f3c..."  stored: 9f3c... : hash("9f3c..." + "summer2024") = 41be...
  bob   -> salt = "b027..."  stored: b027... : hash("b027..." + "summer2024") = c5d9...

With salting, the two records are now different despite the shared password, so an attacker cannot tell they match and cannot reuse work between them. A rainbow table built for bare summer2024 finds nothing, because what was actually hashed was the salt-plus-password combination unique to each user. The salts are stored in the clear, which is fine — their value comes from being unique and unpredictable, not from being hidden.

[!NOTE] A pepper is a related but distinct idea: a secret value added to all passwords and kept outside the database (for example, in application config or an HSM). Unlike a salt, a pepper is secret and shared, so it is not stored with the hash. Peppers complement salts; they do not replace them.

Cybersecurity relevance

Salting is a baseline requirement for password storage and a frequent finding in security reviews. An unsalted password store is severely weakened: a single leak exposes every user of a common password at once and lets attackers use precomputed tables to reverse many hashes almost instantly. Salting forces attackers into slow, per-account guessing. In CTF forensics and cracking challenges, you will routinely see hashes presented with their salts (the modular crypt format embeds the salt in the string), and correct handling means feeding both the salt and the candidate password to your hashing step. Modern password-hashing functions generate and manage salts for you automatically — a strong reason to use them rather than rolling your own scheme.

Common mistakes

  • Reusing one global salt for every password — this restores deduplication and lets a single table target that salt.
  • Thinking the salt must be secret — it need only be unique and random; it is stored with the hash.
  • Believing salting alone is enough — it stops precomputation and dedupe but not per-guess speed; you still need a slow hash.
  • Generating salts non-randomly (e.g. from the username), making them predictable and precomputable.

Key takeaways

  • A salt is a unique, random, non-secret value combined with a password before hashing and stored alongside the resulting hash.
  • Salts defeat rainbow tables and precomputation, because a separate table would be needed for every salt.
  • Salts make identical passwords produce different hashes, removing deduplication and cross-account shortcuts.
  • Salting does not slow a single guess; pair it with a slow password-hashing function for full protection.

Check your understanding

  • Why does a unique per-password salt make a precomputed rainbow table useless?
  • If a salt is stored in plaintext next to the hash, how does it still improve security?
FINISH THIS LESSON

Sign in to save your progress.