Back to Learn
HashingCryptographyIntermediate

Password Hashing: bcrypt vs Argon2 vs SHA-256

Why can't you just SHA-256 a password? Understanding the difference between hashing for data integrity versus hashing for password storage is critical for any developer.

December 2, 20249 min read

The core problem with password storage

Passwords should never be stored in plaintext — if your database is breached, every user's password is immediately exposed. Instead, you store a hash of the password and verify logins by hashing the entered password and comparing.

But not all hash functions are equal for this purpose. The key insight is that password hashing has fundamentally different requirements than general-purpose hashing:

  • General hashing (SHA-256, SHA-512): should be fast — used for file integrity, digital signatures, checksums.
  • Password hashing (bcrypt, Argon2): should be slow — deliberately difficult to compute to resist brute-force attacks.

Why you can't just SHA-256 a password

SHA-256 can compute about 10 billion hashes per second on a modern GPU. An attacker with a breached database of SHA-256-hashed passwords can attempt the entire rockyou.txt wordlist (14 million common passwords) in under 2 milliseconds.

Worse, rainbow tables — precomputed hash lookup tables — mean attackers don't even need to compute hashes in real time. They can look up a SHA-256 hash and retrieve the original password in microseconds if it's a common word or phrase.

✗ Never do this

const hash = sha256(password) — this is not password hashing. It's a catastrophic security mistake.

bcrypt — the battle-tested standard

bcrypt was designed specifically for password hashing in 1999 by Niels Provos and David Mazières. Its key innovation is a cost factor (also called work factor) — a configurable parameter that determines how much computation is required.

// Node.js example
const bcrypt = require('bcrypt');
const saltRounds = 12; // cost factor

const hash = await bcrypt.hash('mypassword', saltRounds);
// $2b$12$eW6tR.../  (60-char string including salt)

const valid = await bcrypt.compare('mypassword', hash); // true

With cost factor 12, bcrypt takes roughly 250ms to hash a single password on modern hardware. That's imperceptibly slow for a user logging in once, but devastating for an attacker trying billions of guesses — at 4 hashes/second instead of 10 billion.

bcrypt also automatically handles salting (more on that below) and encodes the salt, cost factor, and hash in a single output string — making it simple and hard to misuse.

Argon2 — the modern winner

Argon2 won the Password Hashing Competition (PHC) in 2015 and is now the recommended algorithm for new projects. It improves on bcrypt in one crucial dimension: memory hardness.

bcrypt is CPU-bound — attackers can parallelize it cheaply using GPUs and ASICs. Argon2 requires a configurable amount of memory per computation. Since GPU memory is expensive and limited, this makes parallel attacks dramatically more costly.

// Node.js with argon2 package
const argon2 = require('argon2');

const hash = await argon2.hash('mypassword', {
  type: argon2.argon2id,  // recommended variant
  memoryCost: 65536,      // 64 MB
  timeCost: 3,            // 3 iterations
  parallelism: 4          // 4 threads
});

const valid = await argon2.verify(hash, 'mypassword'); // true

Argon2 has three variants: argon2d (GPU-resistant), argon2i (side-channel resistant), and argon2id (hybrid — recommended for passwords).

Side-by-side comparison

AlgorithmSpeedMemory-hardSalt built-inRecommended?
Argon2idSlow (tunable)✓ Yes✓ Yes✓ Best choice
bcryptSlow (tunable)✗ No✓ Yes✓ Good (widely supported)
scryptSlow (tunable)✓ YesManual✓ Acceptable
PBKDF2Slow (tunable)✗ NoManual⚠ Last resort
SHA-256Fast (10B/s)✗ No✗ No✗ Never
MD5Fastest✗ No✗ No✗ Never

What is salting and why does it matter?

A salt is a random value added to a password before hashing. Both bcrypt and Argon2 generate this automatically. Without salting, two users with the same password would have the same hash — making a rainbow table attack viable.

With salting, even if two users have the password "password123", their hashes are completely different because each has a unique salt. Rainbow tables become useless, and attackers must brute-force each hash individually.

Practical recommendations

  • New projects: Use Argon2id with at minimum 64MB memory cost, 3 iterations, and 1 parallelism.
  • Existing projects: If you're already on bcrypt with cost 10+, you're fine. Consider upgrading to Argon2 on next login (re-hash after successful bcrypt verification).
  • Absolute minimum: If your platform only supports PBKDF2 (common on some cloud services), use at least 600,000 iterations with SHA-256.
  • Upgrading: Never rehash all passwords at once — users can't log in if you corrupt their hashes. Migrate progressively: hash with Argon2 after each successful login, removing old bcrypt hashes.

Want to see how hash functions compare in practice? Try our Hash Generator to compute SHA-256 and SHA-512 hashes, and check password strength to understand how entropy affects crack resistance.