⚿ Cryptography

Password Hashing Explained: bcrypt, scrypt, Argon2, and PBKDF2

Password hashing is the practice of storing user passwords as irreversible, deliberately slow cryptographic values so that a database breach does not immediately expose everyone's credentials. Unlike general-purpose hashes designed for speed, password hashing functions are engineered to be expensive to compute, frustrating attackers who try to guess billions of passwords. This article explains the four algorithms every security engineer should know: bcrypt, scrypt, Argon2, and PBKDF2.

Why General-Purpose Hashes Fail for Passwords

A natural but dangerous instinct is to store a password as its SHA-256 hash. This is unsafe for two reasons:

  • Speed is the enemy. SHA-256 is designed to be fast, so an attacker with modern GPUs or ASICs can compute billions of guesses per second against a stolen hash.
  • No per-user variation. Identical passwords produce identical hashes, so attackers can use precomputed rainbow tables and crack many accounts at once.

Password hashing functions fix both problems with two core ideas: salting and deliberate slowness (work factors). They are specialized key derivation functions (KDFs) tuned for password storage.

Salting: Defeating Precomputation

A salt is a unique, random value generated per password and stored alongside the hash. It is mixed into the hashing process so that:

  • Two users with the same password get different stored hashes.
  • Precomputed rainbow tables become useless, because an attacker would need a separate table per salt.

Salts do not need to be secret, only unique and unpredictable, typically 16 bytes from a secure random source. A related optional secret is a pepper, a site-wide secret kept outside the database that adds a further layer if the database alone is leaked.

PBKDF2: Iterated Hashing

PBKDF2 (Password-Based Key Derivation Function 2) is the oldest of these standards. It repeatedly applies a pseudorandom function, almost always HMAC over a hash like SHA-256, for a configurable number of iterations. Each iteration feeds into the next, so the total cost scales with the iteration count.

  • Strengths: simple, standardized, widely available, and FIPS-approved, which matters in regulated environments.
  • Weakness: it is only computationally hard, not memory-hard. Attackers can parallelize it cheaply on GPUs and ASICs because each guess uses very little memory.

To use PBKDF2 well, set the iteration count as high as your performance budget tolerates, using a high-entropy random salt per password.

bcrypt: Adaptive and Battle-Tested

bcrypt is based on a modified version of the Blowfish cipher's expensive key setup. Its defining feature is a tunable cost factor (work factor): each increment doubles the work, so the algorithm can be made slower as hardware improves.

  • Strengths: mature, widely deployed, resistant to naive GPU acceleration because its algorithm accesses a moderate amount of memory in a cache-unfriendly pattern.
  • Limitations: it uses a fixed, small memory footprint (around 4 KB), so it is not strongly memory-hard, and it truncates passwords beyond 72 bytes, which callers must handle.

bcrypt remains a solid, conservative choice, and its long track record makes it a safe default where Argon2 is unavailable.

scrypt: Introducing Memory-Hardness

scrypt was designed to defeat custom hardware attacks by being memory-hard: it requires a large, tunable amount of memory as well as CPU time. Because memory is expensive to replicate across thousands of parallel cracking units, memory-hardness dramatically raises the cost of large-scale attacks.

scrypt exposes parameters for CPU/memory cost (N), block size (r), and parallelism (p). Tuning these lets defenders balance security against server resources. Its main drawback is that misconfiguration is easy, and choosing safe parameters requires care.

Argon2: The Modern Recommendation

Argon2 won the Password Hashing Competition and is the recommended default for new systems. It is strongly memory-hard and highly configurable, with three variants:

  • Argon2d maximizes resistance to GPU cracking but uses data-dependent memory access, which can be vulnerable to side channels.
  • Argon2i uses data-independent memory access, resisting side-channel attacks but offering slightly less brute-force resistance.
  • Argon2id is a hybrid combining both, and is the generally recommended variant for password storage.

Argon2 takes three key parameters:

  1. Memory cost: how much RAM each hash consumes (for example, tens to hundreds of megabytes).
  2. Time cost: the number of iterations over that memory.
  3. Parallelism: the number of lanes/threads used.
# Conceptual parameters for Argon2id (tune to your hardware)
hash = argon2id(
    password = user_password,
    salt     = random_16_bytes,
    memory   = 64 * 1024,   # 64 MiB
    time     = 3,           # iterations
    parallelism = 4         # lanes
)

Tuning aims for a hash that takes a noticeable fraction of a second on your server, making mass guessing prohibitively expensive while keeping legitimate logins responsive.

Choosing and Operating a Password Hash

Practical guidance for deploying password hashing:

  1. Prefer Argon2id for new systems; bcrypt and scrypt are strong alternatives, and PBKDF2 is acceptable where standards compliance requires it.
  2. Always use a unique random salt per password, stored with the hash.
  3. Calibrate work factors to your hardware so each hash takes a meaningful fraction of a second, and raise them over time.
  4. Store the parameters (algorithm, salt, cost) with each hash so you can verify and later upgrade.
  5. Use constant-time comparison when verifying, and rehash with stronger parameters when users next log in.

Password hashing is a defensive control: its goal is to buy time and raise attacker cost after a breach, complementing rate limiting, multi-factor authentication, and breach monitoring.

Key Takeaways

  • Password hashing uses deliberately slow, salted algorithms so a database breach does not instantly reveal passwords.
  • Never use fast general-purpose hashes like plain SHA-256 for passwords; they are trivially parallelized by attackers.
  • Salting defeats rainbow tables, and work factors make each guess expensive; memory-hardness further defeats GPU and ASIC attacks.
  • Argon2id is the modern recommendation, with bcrypt and scrypt as strong choices and PBKDF2 for compliance-driven contexts.
  • Tune parameters to your hardware, store them with each hash, and increase them as computing power grows.
password-hashingargon2bcryptkey-derivationcryptography

Frequently asked questions

What is password hashing?

Password hashing converts a password into a fixed-length value using a one-way function so that the original password is never stored directly. If a database is breached, attackers see only hashes, and a well-designed password hash is deliberately slow and salted to make recovering the original passwords expensive.

Why shouldn't I use SHA-256 or MD5 to hash passwords?

General-purpose hashes like SHA-256 and MD5 are designed to be extremely fast, which lets attackers test billions of password guesses per second on modern hardware. Password hashing should instead use slow, memory-hard functions like Argon2, bcrypt, or scrypt that are specifically built to resist large-scale guessing.

What is the difference between bcrypt, scrypt, and Argon2?

bcrypt is a proven, widely supported password hash with an adjustable cost factor but limited memory use. scrypt adds memory-hardness to resist GPU and ASIC attacks, and Argon2 is a modern winner of the Password Hashing Competition that tunes time, memory, and parallelism, making it the generally recommended choice.

What is a salt in password hashing?

A salt is a unique random value added to each password before hashing so that identical passwords produce different hashes. Salts prevent attackers from using precomputed rainbow tables and force them to crack each password individually, and they can be stored in plaintext alongside the hash.

What is PBKDF2 and how does it compare to Argon2?

PBKDF2 is a key-derivation function that slows guessing by applying a hash function many times, and it is widely supported and standardized. However, it is not memory-hard, so it is more vulnerable to GPU and ASIC attacks than Argon2 or scrypt, which are preferred when available.

How do I store passwords securely?

Store passwords using a slow, salted, memory-hard hashing function such as Argon2id, bcrypt, or scrypt, with parameters tuned to be as costly as your system can tolerate. Never store plaintext or fast unsalted hashes, and use a unique salt per password, which modern password-hashing libraries handle automatically.

Try it hands-on

K0G is an open toolkit of browser-based security utilities — hashing, encoding, JWT, certificates, crypto and more, all running locally in your browser.

Explore the tools →