🔐 encrypt.tools
KDF · PASSWORDS

Argon2id vs bcrypt vs scrypt vs PBKDF2: Password Hashing in 2026

Which password hashing algorithm should you use? A practical comparison of Argon2id, bcrypt, scrypt and PBKDF2 with OWASP-recommended parameters, hash formats, pitfalls and migration steps.

By ··6 min read

Key takeaways

  • Argon2id is the default choice for new systems. OWASP's minimum: 19 MiB of memory, 2 iterations, 1 degree of parallelism (m=19456,t=2,p=1).
  • bcrypt is still acceptable at cost 10 or higher (12 is common today), but it silently ignores everything after the first 72 bytes of a password.
  • scrypt is a solid memory-hard alternative: N=2^17, r=8, p=1 (128 MiB).
  • PBKDF2 is the choice when you need FIPS-140 compliance: use HMAC-SHA-256 with at least 600,000 iterations.
  • Never store passwords with plain SHA-256, MD5 or reversible encryption. Fast hashes run at billions of guesses per second on a GPU; password hashes are designed to run at thousands.

When a database leaks, the password hashes are what stand between the attacker and your users' accounts — including their accounts on *other* sites, since people reuse passwords. A good password hash cannot stop a weak password from being guessed, but it can make each guess so expensive that guessing millions of them becomes impractical.

That is the difference between a general-purpose hash and a password hash. SHA-256 is built to be fast: a single high-end GPU computes on the order of 20 billion SHA-256 hashes per second, and over 100 billion MD5 hashes. Password hashing functions are built to be slow on purpose, to use a unique salt per password so identical passwords produce different hashes, and — in the modern ones — to require lots of memory, which is the resource GPUs and ASICs have least of per core.

The short answer

Password hashing algorithms ranked
AlgorithmYearMemory-hardRecommended minimum (OWASP)Use when
Argon2id2015Yesm=19 MiB, t=2, p=1Default for new systems
scrypt2009YesN=2¹⁷, r=8, p=1 (128 MiB)Argon2 unavailable
bcrypt1999No (4 KiB)Cost 10 (12 common)Existing systems, broad library support
PBKDF22000NoSHA-256 × 600,000FIPS-140 compliance required

Argon2id: the modern default

Argon2 won the Password Hashing Competition in 2015 and is standardized in RFC 9106. It comes in three variants: Argon2d (maximizes GPU resistance but is vulnerable to side-channel attacks), Argon2i (side-channel resistant but weaker against trade-off attacks), and Argon2id, a hybrid that is the one you should use. It has three tuning knobs:

  • m — memory in KiB. This is the main defense: each guess must fill and repeatedly read this much RAM.
  • t — number of passes over that memory (time cost).
  • p — degree of parallelism (lanes).

OWASP's baseline is m=19456 (19 MiB), t=2, p=1. It lists equivalent trade-offs if memory is scarce: m=12288,t=3, m=9216,t=4 or m=7168,t=5. RFC 9106 itself suggests far more — 64 MiB with t=3 as its "memory-constrained" option — so treat OWASP's numbers as a floor and raise memory until a login takes roughly a quarter to half a second on your servers.

Argon2 hashes are stored in the PHC string format, which keeps the algorithm, version, parameters and salt next to the hash:

$argon2id$v=19$m=19456,t=2,p=1$cGVwcGVyLWZyZWUtc2FsdA$lPcgsmHYK3WDaSWfvRMBw7k4nzggT3pkOBenX1/wiN8
 └ variant  └ ver └ parameters   └ salt (Base64)        └ 32-byte hash (Base64)

That is the Argon2id hash of correct horse battery staple with the salt pepper-free-salt (Base64 cGVwcGVyLWZyZWUtc2FsdA). Reproduce it in our Argon2 hash generator by entering that salt with t=2, m=19456, p=1, or with the reference command-line tool:

printf 'correct horse battery staple' | argon2 pepper-free-salt -id -t 2 -k 19456 -p 1 -l 32 -e

In production, use a random 16-byte salt per password — the library's hash() function does this for you. Because parameters travel with the hash, you can raise them later without breaking old hashes.

bcrypt: old, battle-tested, with one big gotcha

bcrypt (1999) is built on the Blowfish cipher's expensive key setup. Its single parameter, the cost, is a base-2 logarithm: each +1 doubles the work. It uses only 4 KiB of memory, so it is not memory-hard, but it has resisted GPU acceleration better than its age suggests and is available in every language.

$2b$12$JA3RR0XJEU1ovh9epjsDDeeOCsIvM4kHQkSsjN/mhQ3O4a95Ckh16
 │  │  └ 22 chars: 128-bit salt
 │  └ cost 12 → 2^12 = 4096 rounds of key expansion
 └ version ($2b$ is current)       └ last 31 chars: 184-bit hash

The 72-byte limit

bcrypt only uses the first 72 bytes of the password. Anything after that is silently ignored, so two long passwords that share a 72-byte prefix produce matching hashes. We verified this with bcryptjs: a hash of 72 × a + XYZ validates against 72 × a + different. This matters for long passphrases and for multi-byte UTF-8 text, where 72 bytes can be as few as 18 emoji.

OWASP's minimum is cost 10; cost 12 is a common modern choice. Measure it: pick the highest cost that keeps a hash around 250 ms on your hardware.

scrypt: memory-hard and mature

scrypt (Colin Percival, 2009; RFC 7914) was the first widely used memory-hard function and is the KDF behind many cryptocurrency wallets and Litecoin's proof of work. Its parameters are N (CPU/memory cost, a power of two), r (block size) and p (parallelism). Memory used is roughly 128 × N × r bytes, so OWASP's recommended N=2^17, r=8, p=1 needs 128 MiB per hash.

scrypt is a good choice where Argon2 is not available — for example Node.js ships crypto.scrypt natively. Its weakness is that memory and time are coupled through N, so you cannot raise one without the other.

PBKDF2: the compliance choice

PBKDF2 (RFC 8018, NIST SP 800-132) simply iterates HMAC many times. It needs almost no memory, which makes it the most GPU-friendly of the four — and therefore the weakest per unit of server time. Its advantage is that it is approved under FIPS 140, so regulated environments often must use it. It is also built into Web Crypto, which is why browser-side tools (including our own AES encryptor) use it for key derivation.

OWASP iteration minimums for 2026:

  • PBKDF2-HMAC-SHA-256: 600,000 iterations
  • PBKDF2-HMAC-SHA-512: 210,000 iterations
  • PBKDF2-HMAC-SHA-1: 1,300,000 iterations (only for legacy compatibility)

Mistakes that undo a good algorithm

  • Reusing or omitting salts. Every password needs its own random salt (16 bytes is plenty). All four algorithms handle this for you when you use the library's high-level hash() function — don't roll your own.
  • Comparing hashes with `==`. Use the library's verify() function, which compares in constant time.
  • Setting parameters once and forgetting them. Hardware gets faster. Re-measure every year or two and raise the cost; the stored parameters make this painless.
  • Unbounded password length with Argon2 or PBKDF2. Accept long passwords, but cap input (for example at 1,024 characters) so attackers can't submit megabyte-long passwords to burn CPU.
  • Encrypting instead of hashing. If the server can decrypt passwords, so can whoever steals the server's key.

A pepper — a secret key stored outside the database (in an HSM, KMS or environment secret) and mixed in with HMAC — adds protection if only the database leaks. It is optional and complements, never replaces, a proper password hash.

Migrating from MD5, SHA-1 or unsalted SHA-256

You cannot rehash old hashes without the original passwords — but you don't have to wait for every user to log in:

  1. Wrap immediately. For every legacy hash, compute argon2id(old_hash) and store that, marked with a version flag. Now every account is protected by Argon2id today.
  2. Verify through both layers. At login, compute the legacy hash of the submitted password, then verify it against the wrapped Argon2id hash.
  3. Upgrade on login. On successful login, replace the wrapped hash with a direct argon2id(password).
  4. Expire stragglers. After a set period, force password resets for accounts that never logged in.

Unsure how strong your users' passwords need to be in the first place? See how password entropy is calculated.

FREQUENTLY ASKED

Frequently asked questions

Is Argon2 better than bcrypt?

+

For new systems, yes. Argon2id is memory-hard, which makes GPU and ASIC cracking far more expensive, it has no 72-byte password limit, and its parameters can be tuned independently. bcrypt remains acceptable for existing systems at cost 10 or higher.

What bcrypt cost factor should I use in 2026?

+

OWASP's minimum is 10. Many applications use 12. The right value is the highest cost that keeps a single hash at roughly 250 ms on your production hardware; each increase of 1 doubles the time.

How many PBKDF2 iterations are recommended?

+

OWASP recommends at least 600,000 iterations for PBKDF2-HMAC-SHA-256 and 210,000 for PBKDF2-HMAC-SHA-512.

Is SHA-256 good enough for passwords?

+

No. SHA-256 is designed to be fast, so a single GPU can test billions of guesses per second. Even with a salt, that is far too fast. Use Argon2id, scrypt, bcrypt or high-iteration PBKDF2.

Do I need to store the salt separately?

+

No. Modern formats such as the Argon2 PHC string and the bcrypt modular crypt string embed the salt and parameters in the stored hash. Salts are not secret; they only need to be unique.

TOOLS IN THIS GUIDE

Tools used in this guide

KEEP READING