When a user creates an account, the server has to store something that proves the user knows their password. Storing the password directly is a non-starter; one database breach and millions of accounts are exposed. The standard practice is to apply a password hashing function and store only the hash. When the user logs in next time, the server hashes the entered password and compares.
The trouble is that ordinary cryptographic hash functions are too fast. SHA-256 runs at billions of evaluations per second on a modern GPU. A weak password hashed with SHA-256 falls in seconds. Even a salted SHA-256 falls in days for any password short enough to be memorable.
Three families of slow, deliberately expensive password hashing functions have dominated the field over the past two decades: bcrypt, scrypt, and Argon2id. Each one tried to solve specific problems with the previous generation. In 2026, the consensus is clear, but the right answer depends on your constraints.
This article walks through each function, explains why the field moved from one to the next, and gives concrete deployment guidance.
Why Slow Is Good
The defender computes the hash once per login. The attacker, after stealing the database, computes the hash billions or trillions of times to crack each password. The defender's cost is amortized over rare events; the attacker's cost is paid for every guess. So the defender wants the hash to be slow, and to slow attackers more than themselves.
Two ways to slow things down: make the function take a lot of time, and make it take a lot of memory. Memory is the more interesting axis because attackers cannot easily scale it. Custom hardware (ASICs, FPGAs, GPUs) can do trillions of fast hash operations per second, but cramming gigabytes of memory onto each parallel unit is expensive.
A function is called memory-hard if any reasonable implementation must use a target amount of fast-access memory. The cost of attacks scales with that memory.
Defender constraint: a password hash should take 100 to 500 milliseconds and a few hundred megabytes of memory at most. Above that, login latency suffers and busy servers cannot keep up.
Attacker constraint: a password hash should be hard to parallelize on cheap hardware. Memory-hard functions force the attacker to pay for memory, which limits parallelism per dollar.
bcrypt: The Original Slow Hash
bcrypt was published in 1999 by Niels Provos and David Mazieres at the USENIX Annual Technical Conference. It uses a modified Blowfish cipher in a loop with adjustable cost. The cost parameter is the base-2 logarithm of the number of internal iterations: cost = 10 means 1024 iterations, cost = 12 means 4096, and so on.
bcrypt has a 72-byte input limit (the Blowfish key schedule size). Passwords longer than 72 bytes are silently truncated, which is a footgun. Some implementations pre-hash long passwords with HMAC-SHA-256, which gets around the limit but breaks compatibility.
The output is a 60-character ASCII string with a recognizable $2b$ prefix. The cost is encoded in the string, so verifying a password automatically uses the right cost.
bcrypt is CPU-hard but not memory-hard. It uses about 4 KB of internal state, which fits comfortably in cache. Modern attackers can run thousands of bcrypt instances in parallel on a single GPU at maybe 100,000 guesses per second per dollar of hardware. That is fast enough to crack weak passwords; it does not scale enough to dent a strong one.
bcrypt is supported by every major web framework. PHP's password_hash defaults to bcrypt. Ruby on Rails has it built in. Many Java frameworks and Node.js libraries default to it. Its ubiquity is its main asset in 2026.
Costs (as of 2026): cost = 12 takes about 250 ms on a fast CPU and about 4 KB of memory. Most deployments are still on cost = 10 or cost = 11 (60-100 ms). Targets should rise.
scrypt: Memory-Hard Foundation
scrypt was introduced by Colin Percival in 2009 and standardized in IETF RFC 7914. It is the first widely-deployed memory-hard password hashing function. We covered it in detail in scrypt: Memory-Hard Password Hashing Explained.
scrypt fills an array of N blocks (each 128 * r bytes) and walks the array in a pseudorandom order, mixing as it goes. To compute the answer, you must either store all N blocks in memory or recompute discarded blocks at huge time cost.
Parameters: N is the cost (must be a power of two), r is the block size factor (default 8), p is the parallelism factor (default 1). Memory usage is approximately 128 N r bytes.
For N = 2^14 (16,384) and r = 8: about 16 MB of memory and ~30 ms per hash on modern CPU. For N = 2^17 (131,072): about 128 MB and ~300 ms.
scrypt's memory-hardness makes it dramatically harder to parallelize than bcrypt. A custom ASIC for scrypt at typical parameters costs orders of magnitude more per evaluation than a bcrypt ASIC.
But scrypt has known weaknesses:
Data-dependent memory access patterns leak information through cache timing. An attacker with code-execution access on the same CPU can sometimes infer bits of the password.
Parameter complexity. Three knobs (N, r, p) with non-obvious interactions confuses operators, who often pick weak values.
Memory-time tradeoff. Researchers showed that for some parameter choices, attackers can use less memory by spending more time, with a favorable tradeoff.
These critiques motivated the Password Hashing Competition.
Argon2id: The Current Champion
Argon2 was the winner of the Password Hashing Competition (2013-2015). It was designed by Alex Biryukov, Daniel Dinu, and Dmitry Khovratovich and standardized in IETF RFC 9106 in 2021. Argon2id is the recommended variant.
Argon2 has three variants:
Argon2d uses data-dependent memory access. Maximally resistant to GPU attacks but vulnerable to side-channel attacks if attackers can observe cache behavior.
Argon2i uses data-independent memory access. Side-channel safe but slightly weaker against time-memory tradeoff attacks.
Argon2id is a hybrid: data-independent in the first half, data-dependent in the second. This gets the benefits of both. It is the recommended choice for password hashing.
Parameters: m is memory in KiB, t is time (number of passes), p is parallelism, plus a salt and optional secret pepper.
For m = 65536 (64 MB), t = 3, p = 4: about 250 ms on modern CPU and 64 MB of memory. This is OWASP's current minimum recommendation.
For m = 19456 (19 MB), t = 2, p = 1: about 100 ms and 19 MB. This is the RFC 9106 minimum recommended baseline.
Argon2id uses BLAKE2b internally as its compression function, which is fast on commodity hardware. It does not require AES-NI or SHA-NI extensions to be performant.
Argon2id has clear parameters, a strong security analysis, modern side-channel resistance, and is now supported by all major language ecosystems (Python, Go, Rust, Java, JavaScript, .NET, Ruby).
For new deployments in 2026, Argon2id is the default recommendation.
The Comparison Table
For a target latency of 250 ms on a modern CPU:
bcrypt at cost 12: ~4 KB memory, ~10 GB/s GPU crack rate per dollar, no side-channel resistance.
scrypt at N=2^17, r=8, p=1: 128 MB memory, ~50 MB/s GPU crack rate per dollar, weak side-channel resistance.
Argon2id at m=64 MB, t=3, p=4: 64 MB memory, ~5 MB/s GPU crack rate per dollar, strong side-channel resistance.
The crack rate numbers are rough estimates from public benchmarks. The relative ratios are what matter: Argon2id is roughly 1000x harder to crack than bcrypt and 10x harder than scrypt at equivalent defender latency.
What About PBKDF2?
PBKDF2, defined in NIST SP 800-132, is the granddaddy of password hashing. It is approved for FIPS use and required by many compliance regimes. We discuss it briefly in our scrypt and Argon2id articles.
PBKDF2's only defense is iteration count. It is not memory-hard. Modern attackers can run billions of PBKDF2-HMAC-SHA-256 evaluations per second on a GPU.
OWASP's 2026 PBKDF2 recommendation is 600,000 SHA-256 iterations or 210,000 SHA-512 iterations. Even at those rates, PBKDF2 is dramatically weaker than Argon2id.
If you must use PBKDF2 (FIPS compliance, regulated environments), push the iteration count as high as your latency budget allows. Pre-hashing the password with HMAC-SHA-256 before PBKDF2 limits the input length but does not improve security.
Practical Deployment Choices
For a new web application in 2026, use Argon2id. Set m = 65536 KiB (64 MB), t = 3, p = 4. Use libsodium, argon2-cffi (Python), argon2 (Rust), Argon2id (Java with the BouncyCastle provider), or your language's well-maintained equivalent.
For an existing bcrypt deployment, leave existing hashes as bcrypt and migrate users on next login by re-hashing with Argon2id. Use a column to track which algorithm a hash uses, and update on successful authentication.
For an existing scrypt deployment, the urgency is lower because scrypt is still strong. Migrate when convenient.
For PBKDF2 in FIPS-mandatory environments, push iterations high (600,000+ SHA-256). Plan for Argon2 to become FIPS-approved in the next few years and migrate then.
For password hashing on devices with strict memory limits (smart cards, low-end IoT), bcrypt is sometimes the only option. The 4 KB memory footprint fits where Argon2id's 19 MB does not.
For non-password key derivation (e.g., from a high-entropy master key), do not use any of these. Use HKDF or SP 800-108. See HKDF (RFC 5869) Line by Line and Two-Step Key Derivation.
Side Channels and Implementation Quality
Constant-time comparison is essential. When the server checks the user's password against the stored hash, it computes the candidate hash and compares. If the comparison is byte-by-byte and bails on the first mismatch, an attacker can recover the hash byte-by-byte through timing. Use constant-time compare functions (libsodium's sodium_memcmp, Rust's subtle::ConstantTimeEq, etc.).
Memory zeroization. Password and intermediate state should be zeroed after use. Most libraries do this internally, but if you handle the password yourself, use a secure-erase function (Rust's zeroize crate, C's memset_s, etc.).
Salt handling. Salts must be random and unique per password. Re-using salts across users (or worse, no salt at all) lets attackers precompute rainbow tables. All three of bcrypt, scrypt, and Argon2id include salt management in their standard APIs; let the library generate a fresh salt for each hash.
Pepper (server-side secret). An optional pepper, stored separately from the database (e.g., in an HSM), strengthens the hashes against database-only breaches. Argon2id supports a separate "secret" parameter for this. This is a defense-in-depth measure; it does not replace good password hashing.
Post-Quantum Aspects
Password hashing is one of the more quantum-resilient parts of cryptography because it derives security from password entropy and computational cost, not from algebraic problems Shor's algorithm attacks.
Grover's algorithm gives a quadratic speedup against unstructured search. For a password with effective k bits of entropy, brute force costs 2^k classically and 2^(k/2) with Grover. So a 6-character password (about 30 bits of entropy) is still weak; a strong random 24-character password (about 100 bits of entropy) is still safe.
The memory-hardness of scrypt and Argon2id is unaffected by quantum attacks. The only post-quantum concern is that effective password entropy halves under Grover. The remedy is longer or higher-entropy passwords (passphrases, password manager-generated long random strings), and continuing to use a strong memory-hard function.
For QNSQY's password-based key derivation, Argon2id is used to derive long-term key encryption keys from user passphrases. The work factor is tuned to make brute-force impractical even with quantum-era hardware.
Frequently Asked Questions
Is Argon2id FIPS-approved?
As of 2026, no. PBKDF2 remains the only FIPS-approved password-based KDF. NIST has signaled intent to add Argon2 to its approved list but the formal addition has not happened. For non-FIPS environments, use Argon2id.
What if I need to interoperate with old systems?
Stick with bcrypt or PBKDF2 for compatibility. You can layer Argon2id on top of bcrypt by treating the bcrypt output as input to Argon2id, but this is unusual and complicates audit.
Should I use Argon2d, Argon2i, or Argon2id?
Argon2id. It is the recommended variant in RFC 9106 and OWASP guidance. Argon2d alone is vulnerable to side channels in some environments. Argon2i alone is slightly weaker against time-memory tradeoff attacks.
How often should I update my password hashing parameters?
Review annually. Hardware speeds increase; what was 250 ms last year may be 100 ms next year. Track your average login latency and adjust parameters to keep it in the 200-500 ms range on your target hardware.
What about Yescrypt and other PHC finalists?
Yescrypt is a strong scrypt variant used in some Linux distributions for shadow file hashing. It is not as widely deployed as Argon2id outside of system password files. Catena, Lyra2, and the other PHC finalists have niche deployments. For most uses, Argon2id is the right choice. We cover the competition in The Password Hashing Competition.
Sources
- Biryukov, A., Dinu, D., and Khovratovich, D. "Argon2: New Generation of Memory-Hard Functions for Password Hashing and Other Applications." IACR ePrint 2015/430. https://eprint.iacr.org/2015/430
- Biryukov, A., Dinu, D., and Khovratovich, D. "The Memory-Hard Argon2 Password Hash and Proof-of-Work Function." IETF RFC 9106, September 2021. https://datatracker.ietf.org/doc/html/rfc9106
- Percival, C. and Josefsson, S. "The scrypt Password-Based Key Derivation Function." IETF RFC 7914, August 2016. https://datatracker.ietf.org/doc/html/rfc7914
- Provos, N. and Mazieres, D. "A Future-Adaptable Password Scheme." USENIX Annual Technical Conference, 1999. https://www.usenix.org/legacy/publications/library/proceedings/usenix99/provos/provos_html/index.html
- NIST Special Publication 800-132. "Recommendation for Password-Based Key Derivation: Part 1: Storage Applications." December 2010. https://csrc.nist.gov/pubs/sp/800/132/final
- OWASP Foundation. "Password Storage Cheat Sheet." 2024. https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
Related Articles
- Argon2id Explained: How Modern Password Hashing Works
- scrypt: Memory-Hard Password Hashing Explained
- The Password Hashing Competition: Why Argon2 Won
- Hybrid Encryption: Why Combining Old and New Crypto Is Stronger
- What Is Post-Quantum Cryptography? A Plain-English Guide
Protect Your Data Before Q-Day Arrives
QNSQY's NIST-standardized post-quantum encryption protects files against both current and quantum-era threats.