Password Hashing
Password hashing converts passwords into slow, salted verifiers with Argon2id, bcrypt, or scrypt. Fast hashes and reversible encryption are unsafe for password storage.
Definition
Password hashing applies a one-way, deliberately expensive key derivation function to a password with a unique salt, producing a verifier that cannot be feasibly reversed. The algorithm and cost parameters are chosen so that offline guessing is expensive while legitimate logins remain acceptable.
Context and scope
Password hashing is required for any credential store, including web applications, internal tools, and password-derived encryption keys. It is distinct from encryption and from fast message digests such as SHA-256.
Common misconceptions
- A salted SHA-256 digest is adequate because the salt prevents rainbow tables
- Encrypting passwords is safe when the encryption key is stored securely
Anti-patterns
- Using a single global salt shared by every account
- Lowering the cost factor so much that offline cracking becomes cheap
Related concepts
Related examples
Authoritative source
https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
How password hashing works
A password hash is a one-way, deliberately slow function. The server generates a random per-user salt, combines it with the password, and runs a memory-hard algorithm such as Argon2id, bcrypt, or scrypt. It stores the algorithm, parameters, salt, and digest. Verification recomputes the hash and compares it in constant time. Salts defeat precomputed rainbow tables, while cost parameters make offline guessing expensive.
When to use it
Use password hashing whenever you store a password or derive a key from one. Never encrypt passwords, because encryption is reversible and the key becomes a single point of failure. Use Argon2id where available; bcrypt remains acceptable with a cost of at least 12 and inputs under 72 bytes. Rehash on successful login when parameters change.
Trade-offs
Higher cost slows attackers but also consumes server CPU and memory, creating a denial-of-service lever. Argon2id needs memory tuning per instance, while bcrypt has a 72-byte input limit and no memory hardness. Peppering with a KMS-held secret adds defense in depth but complicates rotation. Hashing does not protect weak passwords from credential stuffing, so add rate limiting and breached-password checks.
Example
import argon2 from 'argon2';
const hash = await argon2.hash(password, {
type: argon2.argon2id,
memoryCost: 19456,
timeCost: 2,
});
const ok = await argon2.verify(hash, candidate);
Misconceptions and related concepts
A salted SHA-256 digest is still far too fast. Encryption is not hashing. A pepper is not a substitute for per-user salts. Truncating passwords silently at 72 bytes weakens bcrypt users with long passphrases. Related concepts include secret management, encryption at rest, and least privilege.