Password Storage Mechanics
Passwords must be stored in a form that cannot be reversed to plaintext. The properties required of a password hashing function differ substantially from those required of a general cryptographic hash. Speed is a liability, not an asset.
Why Speed Is a Problem
When an attacker obtains a database of password hashes, they attempt to reverse them offline by hashing candidate passwords and comparing against the stored values. The candidate list comes from dictionary files, previously leaked password sets, or brute force over a character space.
A modern GPU can compute approximately 10 billion MD5 hashes per second. An 8-character lowercase password has 26^8 = roughly 200 billion combinations. At 10 billion hashes per second, exhaustive search takes about 20 seconds. SHA-256 is similarly fast on GPU hardware: roughly 3 billion hashes per second, extending that same search to around 70 seconds.
A password hashing function designed for storage deliberately introduces computational cost. bcrypt at cost factor 12 runs at approximately 5,000 hashes per second on the same GPU. The same 200 billion candidate space takes 40 million seconds, over a year.
Salting
A salt is a random value generated per password and stored alongside the hash. The hash is computed over the concatenation of the salt and the password.
Salting defeats precomputed attacks. Without a salt, an attacker can precompute hashes for all common passwords once and compare them against every hash in the database in constant time. This is a rainbow table attack. With a per-password salt, the attacker must recompute candidates separately for each hash using that hash's unique salt. Precomputation is no longer reusable across accounts.
Salts do not need to be secret. They are stored in plaintext alongside the hash. Their purpose is uniqueness, not secrecy.
bcrypt
bcrypt was designed by Niels Provos and David Mazieres in 1999 specifically for password hashing. It incorporates a cost factor that controls iteration count. Increasing the cost factor by 1 doubles the computation time. The stored output includes the algorithm identifier, cost factor, salt, and hash in a single string, making it self-describing.
bcrypt has a 72-byte input limit. Passwords longer than 72 bytes are silently truncated. Some implementations pre-hash with SHA-256 before bcrypt to remove this limit, which introduces a null-byte truncation risk if not handled carefully.
Argon2 and scrypt
bcrypt is memory-cheap. An attacker can run many bcrypt instances in parallel on GPU hardware because each instance requires minimal memory. Argon2 and scrypt are memory-hard: they require a configurable amount of RAM per computation. GPU cores have limited per-core memory, so memory-hard functions reduce the parallelism advantage of GPU cracking.
Argon2 won the Password Hashing Competition in 2015 and has three variants: Argon2d (GPU resistance), Argon2i (side-channel resistance), and Argon2id (both). Argon2id with a minimum of 64 MB memory cost and 3 iterations is the current OWASP recommendation for new systems.
The cost parameters for any of these functions must be tuned to the server's hardware so that a single hash computation takes 100 to 300 milliseconds. That is imperceptible to a user logging in but multiplies the cost of offline cracking by the same factor as the legitimate computation time.