To store a password safely you hash it with a deliberately slow algorithm and a unique random salt per user — you never encrypt it, and you never store it in plaintext. Password salt and hash are the two halves of the standard defense: the hash makes the stored value irreversible, and the salt makes precomputed attacks useless. Getting this right is one of the highest-leverage security decisions a team makes, because a leaked user table is inevitable often enough that you should design for it.
The core insight is that you never actually need to know a user's password. You only need to check whether the password they typed at login matches the one they set. That check works fine against a hash, which means the original password never has to be recoverable — and that is a feature, not a limitation.
Hashing versus encryption
People conflate these constantly, so it is worth being precise. Encryption is reversible: with the key, you can recover the original. Hashing is a one-way function: you can compute the hash from the input, but you cannot feasibly recover the input from the hash.
Passwords must be hashed, not encrypted. If you encrypt passwords, the key that decrypts them exists somewhere, and whoever gets both the database and the key gets every password in cleartext. With a proper one-way hash, there is no key to steal — even you, the operator, cannot reverse it. When a service emails you your actual password after you click "forgot password," that is a red flag that they stored it recoverably, which is a serious mistake.
What a hash alone doesn't solve
A plain hash has a fatal weakness against real attackers: it is deterministic. The same password always produces the same hash. That enables two attacks.
First, rainbow tables — enormous precomputed lookups of hash-to-password for common passwords. If you store an unsalted hash, an attacker just looks it up.
Second, identical passwords are visible. If two users share the same password, their hashes are identical in your database, which leaks information and lets an attacker crack many accounts at once.
This is where the salt comes in.
What the salt does
A salt is a random value, unique per user, that you combine with the password before hashing. Because each user has a different salt, the same password produces a different hash for each of them. That single change defeats rainbow tables entirely — an attacker cannot precompute a table without knowing the salt, and the salt is different for every row. It also means an attacker must attack each password individually instead of cracking the whole table at once.
The salt is not a secret. It is stored right alongside the hash, typically as part of the same encoded string. Its job is not to be hidden but to be unique. A common mistake is using the same salt for every user, or using something predictable like the username — both defeat the purpose. The salt must be generated from a cryptographically secure random source and be unique per password.
Modern password-hashing functions handle the salt for you. When you call bcrypt, it generates a random salt, hashes the password, and returns a single string that embeds the algorithm, the cost factor, the salt, and the hash together. At login you feed the stored string back in, and the library extracts the salt to recompute and compare. You do not manage the salt by hand.
Use a slow, purpose-built hash
Here is the counterintuitive part: for passwords, you want a slow hash. General-purpose hashes like SHA-256 are designed to be fast, which is exactly wrong for passwords, because fast hashing lets an attacker try billions of guesses per second against a stolen database.
Password-hashing functions are deliberately expensive and tunable. The main choices in 2025:
- Argon2 (specifically Argon2id) is the current recommendation from most security bodies. It is memory-hard, meaning it resists attackers using GPUs and custom hardware to parallelize guessing.
- scrypt is also memory-hard and a solid choice.
- bcrypt is older but still widely used and considered acceptable, with a configurable cost factor. Note it truncates inputs beyond roughly 72 bytes, which matters for very long passwords or passphrases.
- PBKDF2 is the weakest of the accepted options and is mainly relevant where compliance mandates it, since it is not memory-hard.
Each has a tunable work factor — bcrypt's cost, Argon2's memory and iteration parameters. Set it so a single hash takes a noticeable fraction of a second on your hardware. That is invisible to a legitimate user logging in once, but it makes mass cracking enormously expensive.
# conceptual bcrypt-style stored value
$2b$12$Nq3...saltsalt...hashhashhashhashhashhash
^ ^ ^--- salt + hash embedded together
| cost factor (2^12 iterations)
algorithm identifier
Putting it together
The full lifecycle:
- Registration: take the password, let the hashing library generate a unique salt, hash with a high work factor, store the resulting string. Discard the plaintext immediately.
- Login: retrieve the stored string, hash the submitted password with the same salt and parameters (the library handles this from the stored string), and compare using a constant-time comparison to avoid timing leaks.
- Upgrade path: when you raise the work factor or change algorithms, rehash a user's password at their next successful login, since that is the only moment you have the plaintext.
Do not invent your own scheme. Use a well-maintained library for your language and let it manage salting, work factors, and comparison. Rolling your own is where the subtle, catastrophic bugs live. If you want to build the surrounding knowledge, our academy covers authentication fundamentals, and the distinction here pairs closely with how symmetric encryption works — remember that encryption and password hashing solve different problems.
FAQ
What's the difference between salting and hashing a password?
Hashing is the one-way transformation that makes the stored value irreversible. Salting adds a unique random value per user before hashing, so identical passwords produce different hashes and precomputed rainbow-table attacks fail. You need both.
Should I encrypt or hash passwords?
Hash them, never encrypt. Encryption is reversible, which means a stolen key exposes every password. Hashing is one-way, so there is no key to steal and the original password cannot be recovered even by you.
Does the salt need to be secret?
No. The salt's job is to be unique per user, not hidden. It is stored alongside the hash. What matters is that it is random and different for every password, which is why it defeats rainbow tables.
Which password hashing algorithm should I use in 2025?
Argon2id is the leading recommendation. scrypt and bcrypt are also acceptable. Avoid fast general-purpose hashes like plain SHA-256 for passwords, and set the work factor so each hash takes a noticeable fraction of a second.