Skip to content

Argon2 vs bcrypt vs scrypt: picking a password KDF in 2026

Memory hardness is the real defense against GPUs and ASICs. How argon2id, bcrypt, scrypt and PBKDF2 compare, how to tune them, and which one to actually pick.

Published on 5 min read

If you are choosing how to store passwords, the decision comes down to one property that most comparison tables bury: memory hardness. Iteration count slows down a CPU. Memory hardness is what defeats the hardware an attacker actually owns. Get that framing right and the rest of the choice falls out almost mechanically.

Why memory hardness is the whole game

An attacker cracking your password database is not running it on a laptop. They are running it on a rack of GPUs, or for a high-value target, custom ASICs. Those devices have staggering parallel compute and very little fast memory per core. A pure-iteration scheme plays directly into that strength: thousands of cores each grinding the same cheap loop.

A memory-hard function breaks that. It forces every single guess to allocate and touch a large block of RAM. Suddenly the attacker's bottleneck is not compute, it is memory bandwidth and capacity, the one resource that does not scale cheaply across thousands of parallel cores. This is why a well-parameterized memory-hard hash is the only thing on this list that makes a serious attacker simply walk away.

argon2id and its three knobs

Argon2 won the Password Hashing Competition for good reason, and argon2id is the variant you want (it blends the side-channel resistance of argon2i with the GPU resistance of argon2d). It exposes three parameters and you should understand all three:

  • m is memory in KiB. This is the important one. More memory means more pain for the attacker. 64 MiB to 128 MiB per hash is a reasonable server target.
  • t is the time cost, the number of passes over memory. Raise it when you cannot afford more memory.
  • p is parallelism, the number of lanes. Match it loosely to the cores you can spare per authentication.

The encoded hash carries all of it, so you can read the parameters straight out of the stored value and you can raise them later without breaking old hashes.

bcrypt: old, still good, one knob

bcrypt is from 1999 and it has aged remarkably well. It has a single cost factor, the work factor in the $2b$12$ prefix, where 12 means 2^12 iterations of its key schedule. It is not formally memory-hard, but its design is deliberately cache-unfriendly, which is the practical reason bcrypt resists GPUs far better than its age suggests. Its real limitation is the 72-byte password truncation, which matters if you allow very long passphrases or pre-hash inputs carelessly. For most applications, bcrypt at cost 12 or higher is genuinely fine. Do not rip it out just because argon2 exists.

scrypt: memory-hard, fiddly to tune

scrypt got there before Argon2 and is memory-hard by design. It has three parameters that interact in ways people get wrong: N is the CPU/memory cost (a power of two, this drives memory use), r is the block size, and p is parallelism. The trap is that tuning is less intuitive than argon2id, and a lot of deployments pick weak N values and quietly lose the memory hardness they thought they had. scrypt is fine if you already run it correctly. If you are choosing fresh, argon2id is easier to get right.

PBKDF2: the weakest of the four

PBKDF2-SHA256 is pure iterated HMAC. No memory cost, nothing. That makes it the most GPU-friendly scheme here by a wide margin, and an attacker cracks PBKDF2 dramatically faster than any of the other three at equivalent effort. It survives because it is everywhere: FIPS-approved, baked into countless standards and disk encryption schemes. If a compliance regime forces PBKDF2 on you, push the iteration count high (hundreds of thousands of SHA-256 iterations, more if you can) and understand you are buying time, not strength.

Tune for 250 to 500 milliseconds

Whatever you pick, the calibration target is the same: each hash should take roughly 250 to 500 milliseconds on your production hardware. That is imperceptible to a user logging in and brutal to an attacker doing it billions of times. Benchmark on the actual box, not your laptop, and re-benchmark when you upgrade hardware. With argon2id, prefer spending the budget on m before t. With bcrypt, raise the cost factor until you hit the latency target. The point is to consume your full latency budget, because every millisecond you leave on the table is a millisecond of attacker speedup.

The cracking reality, and what to pick

Here is the part that should reassure defenders. All four of these are slow on purpose, which means the arithmetic of fast versus slow hashes is entirely in your favor. Against a well-parameterized argon2id hash, an attacker gets a handful of guesses per second per GPU. A real cracking session against that is not a dictionary run, it is a decision to give up on everything except the genuinely weak passwords, and even those come slowly.

So the recommendation, stated plainly: pick argon2id for anything new, tuned to 64 MiB or more and a 250 to 500 ms latency. Keep bcrypt at cost 12+ if you already have it, there is no urgency to migrate. Use scrypt only if you already run it with sane parameters. Reach for PBKDF2 only when something external forces your hand. The strongest move you can make is not the algorithm anyway, it is parameterizing it well and letting passwords be long.

Related articles

MD5 and SHA-1 fall to a GPU in seconds because they are fast and often unsalted. Learn why slow KDFs like bcrypt and Argon2 resist — and what defenders should do.
Why bcrypt drops cracking throughput from billions to thousands per second: the cost factor, its GPU-hostile key schedule, and the 72-byte truncation gotcha.