Back to Blog
Security 2026-04-28

bcrypt vs Argon2: Password Hashing in 2026

Choose the right password hashing algorithm and tune its parameters for production.

Storing passwords as plain text is unthinkable. Storing them with MD5 or SHA-256 is barely better. The right answer is a slow, memory-hard hash.

Why Slow on Purpose

GPU farms hash billions of SHA-256 candidates per second. A slow hash like bcrypt or Argon2 caps that to thousands per second per dollar of hardware. The factor of a million matters during a breach.

bcrypt: The Reliable Veteran

import bcrypt from 'bcrypt';

const hash = await bcrypt.hash(password, 12);

const ok = await bcrypt.compare(password, hash);

The cost factor (12 in 2026) doubles work for each increment. Re-tune every couple of years as hardware improves. bcrypt is GPU-resistant but not memory-hard.

Limitation: 72-byte input cap. Pre-hash with SHA-256 if users might have long passphrases.

Argon2: The Modern Winner

Argon2id won the 2015 Password Hashing Competition and is now OWASP's recommended default.

import argon2 from 'argon2';

const hash = await argon2.hash(password, {

type: argon2.argon2id,

memoryCost: 2 16, // 64 MB

timeCost: 3,

parallelism: 1

});

Memory-hard: GPU and ASIC attacks become much harder because memory dominates the cost.

OWASP 2026 Parameters

  • Argon2id: m=64 MB, t=3, p=1 (or m=19 MB, t=2, p=1 for tighter envs)
  • bcrypt: cost 12 (cost 14 if you can afford 1s+ per login)

When to Pick Each

  • New project, no constraints → Argon2id
  • Existing bcrypt → keep it; rewrap on next login if you must migrate
  • Hardware-constrained (small embedded) → bcrypt; Argon2 needs RAM

Don't Roll Your Own

Use the language's standard library wrappers. Never implement password hashing yourself; the constant-time comparison alone is non-trivial.

For session tokens see [JWT security best practices](https://sdk.is/blog/jwt-security-best-practices).