Search

How Passwords Should Be Stored (Hashing and Salting)

The short answer

Quick answer: Never store passwords themselves. Store a hash: the output of a one-way function that cannot be reversed. Use an algorithm designed specifically for passwords, Argon2id, scrypt or bcrypt, which is deliberately slow and costly to compute, so that an attacker who steals the database can only test guesses slowly. Give every password its own random salt, so identical passwords produce different hashes and precomputed tables are useless. To check a login, hash the submitted password with the stored salt and compare. Use a well-tested library; do not build this yourself.

Assume the database will leak

Password storage is designed around one scenario: an attacker obtains a copy of your user table. It happens through SQL injection, a misconfigured backup, a stolen laptop or an insider. The question is what the attacker can do next.

The stakes go beyond your site. Many people reuse passwords, so a leaked password for a small forum can unlock the same person's email or bank account. Attackers automate this with credential stuffing: trying leaked email and password pairs against other services.

What not to do

Plain text

Storing passwords as written means a breach exposes every account instantly. It also means your own staff, and anyone with access to logs or backups, can read them.

Encryption

Encrypting passwords is better than plain text but still wrong. Encryption is reversible: whoever holds the key can decrypt everything, and the key usually lives on the same systems as the data. You never need to recover a user's password. You only need to check whether a submitted one matches.

A fast hash

A cryptographic hash function such as SHA-256 is one-way: you cannot run it backwards. Storing SHA-256(password) seems reasonable, and it has been the cause of many disastrous breaches.

The problem is that these functions are built to be fast. A modern graphics card can compute billions of them per second. An attacker does not reverse the hash; they guess. They hash every word in a dictionary, every leaked password from earlier breaches, and every common pattern, and compare the results with your table. Most human-chosen passwords fall quickly.

And without salts:

  • Identical passwords have identical hashes, so cracking one cracks every account that shares it.
  • Rainbow tables, huge precomputed lists of hashes for common passwords, turn cracking into a lookup.

Salts

A salt is a random value, generated separately for each password and stored alongside the hash. It is combined with the password before hashing.

UserPasswordSaltStored hash
alicesunshine19f2a...c41d...
bobsunshine107be...e88a...

Same password, different results. That achieves two things:

  • Precomputed tables are useless, because the attacker would need a separate table for every salt.
  • The attacker must attack each account separately. Cracking one tells them nothing about the others.

A salt does not need to be secret. It must be unique and random, generated by a cryptographically secure random number generator. See Salt (cryptography).

A salt alone does not help against guessing a single user's weak password. For that, the hash must be slow.

Slow by design

Password hashing functions (also called key derivation functions) are engineered to be expensive, with an adjustable work factor.

If checking one password takes a tenth of a second, a real user logging in does not notice. An attacker who could have tried billions of guesses per second is now limited to a handful per second per processor. That turns an afternoon's cracking into centuries for any reasonably strong password.

AlgorithmKey propertyNotes
Argon2idMemory-hard: needs a configurable amount of RAM per guessWinner of the Password Hashing Competition; the first choice for new systems
scryptMemory-hardA solid alternative
bcryptCPU-hard, with a cost factorLong proven and widely available; processes only the first 72 bytes of input
PBKDF2Repeats a standard hash many timesNot memory-hard; used where formal compliance requires it

Memory-hardness matters because attackers use graphics cards and custom chips that run thousands of computations in parallel. Requiring a chunk of memory for every guess makes that parallelism expensive. See Argon2.

The OWASP Password Storage Cheat Sheet lists current recommended algorithms and parameters. Consult it for the specific settings, since recommendations are raised as hardware improves.

What gets stored

Modern libraries produce a single self-describing string containing the algorithm, its parameters, the salt and the hash:

$argon2id$v=19$m=19456,t=2,p=1$c29tZXNhbHQ$RdescudvJCsgt3ub+b+dWRWJTmaaJObG
 algorithm     parameters       salt        hash

You store that one string per user. Because the parameters travel with the hash, you can raise the work factor later without breaking old accounts.

The login flow

Registering

  1. Receive the password over HTTPS.
  2. The library generates a random salt and computes the hash.
  3. Store the resulting string. Discard the password.

Logging in

  1. Look up the stored string for that user.
  2. Hash the submitted password with the same salt and parameters.
  3. Compare, using the library's verify function, which takes the same time whether or not the values match.
from argon2 import PasswordHasher

ph = PasswordHasher()

stored = ph.hash(password)          # at registration
ph.verify(stored, submitted)        # at login; raises an error if wrong

if ph.check_needs_rehash(stored):   # parameters have been raised since
    stored = ph.hash(submitted)     # upgrade while we have the password

Upgrading over time. When you increase the work factor or change algorithm, rehash each user's password the next time they log in successfully, since that is the only moment you have it.

Peppers

A pepper is a secret value added to every password hash, stored outside the database, for example in a secrets manager or hardware security module. If only the database is stolen, the hashes cannot be attacked without it. It is an extra layer, not a replacement for salts and a slow hash.

Common mistakes

  • Inventing a scheme. SHA-256(SHA-256(password) + salt) is still fast. See why you should never roll your own crypto.
  • One salt for all users. That defeats the purpose.
  • Logging passwords in request logs or error reports.
  • Emailing passwords. A site that can send you your password is storing it badly.
  • Unnecessary limits. Capping passwords at 16 characters or forbidding symbols suggests poor handling behind the scenes. Allow long passwords and passphrases.
  • Forced periodic changes. Current guidance favours changing a password only when there is evidence of compromise.
  • Leaking via error messages. "No such user" versus "wrong password" tells attackers which accounts exist.

Around the hash

Good storage is one layer. Also:

Frequently asked questions

What is the difference between hashing and encryption?

Encryption is reversible with a key. Hashing is one-way: you cannot recover the input from the output. Passwords should be hashed.

What is a salt?

A random value, unique to each password, mixed in before hashing so that identical passwords give different hashes and precomputed tables do not work.

Is SHA-256 safe for passwords?

Not on its own. It is a sound hash function but far too fast, which lets attackers test billions of guesses per second.

Should I use bcrypt or Argon2?

Argon2id for new systems. bcrypt remains acceptable and is a reasonable choice where Argon2 is unavailable.

Conclusion

Store a salted hash made with a slow, purpose-built algorithm, using a trusted library, and nothing more. The goal is modest and realistic: when the database leaks, make each password guess so expensive that attackers give up on all but the weakest. Then add rate limiting and a second factor so that passwords are not your only defence.

Related articles

Sources and further reading

Usama Muneer

Usama Muneer

Coder, Blogger, Tech Speaker & Web Technologies Enthusiast. Passionate about working on open-source Programming languages & Tools while utilizing my Product Development skills.

Your experience on this site will be improved by allowing cookies Cookie Policy