Bcrypt vs Argon2: Choosing a Password Hashing Algorithm
Storing passwords in plaintext is the fastest way to lose your users' trust. The industry standard is to hash passwords with a slow, salted algorithm designed specifically for this purpose. The two most common choices today are bcrypt and Argon2.
How Password Hashing Works
Unlike general-purpose hash functions (SHA-256, MD5), password hashing algorithms are intentionally slow. They apply the hash function thousands or millions of times so that brute-forcing a stolen database is computationally expensive.
Both bcrypt and Argon2:
- Automatically generate a unique salt per password, preventing rainbow-table attacks.
- Include a cost factor that lets you increase work as hardware gets faster.
- Produce a self-contained hash string that embeds the salt and parameters.
Bcrypt
Bcrypt has been the default choice since 1999. It's battle-tested, widely supported, and understood.
Key characteristics:
- Cost parameter: A single "rounds" value (default 12 = 2¹² iterations). Each increment doubles the time.
- Fixed memory: Bcrypt uses a fixed 4 KB buffer (Eksblowfish state), making it vulnerable to GPU/ASIC attacks that can run many instances in parallel.
- 72-byte limit: Only the first 72 bytes of the password are hashed. Longer passwords are silently truncated.
- Output format:
$2b$12$...— a 60-character string that encodes version, rounds, salt, and hash.
Hash a password with bcrypt using the Auth Toolkit API:
curl -X POST https://auth.toolkitapi.io/v1/auth/hash-password \
-H "X-API-Key: YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"password": "hunter2", "algorithm": "bcrypt", "rounds": 12}'
Argon2
Argon2 won the Password Hashing Competition in 2015 and is the newer, recommended option. It comes in three variants:
| Variant | Resists | Best for |
|---|---|---|
| Argon2d | GPU attacks | Backend services not exposed to timing attacks |
| Argon2i | Side-channel attacks | Login servers where timing matters |
| Argon2id | Both | Recommended default — hybrid of d and i |
Key characteristics:
- Memory-hard: You configure how much RAM each hash requires (e.g., 64 MB). This makes GPU/ASIC attacks dramatically more expensive because GPUs have limited per-core memory.
- Three tuning knobs: memory cost, time cost (iterations), and parallelism (threads).
- No length limit: The full password is always hashed.
- Output format:
$argon2id$v=19$m=65536,t=3,p=4$...— a self-describing string like bcrypt.
curl -X POST https://auth.toolkitapi.io/v1/auth/hash-password \
-H "X-API-Key: YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"password": "hunter2", "algorithm": "argon2"}'
Head-to-Head Comparison
| Feature | Bcrypt | Argon2id |
|---|---|---|
| Year introduced | 1999 | 2015 |
| Memory usage | Fixed 4 KB | Configurable (e.g., 64 MB) |
| GPU/ASIC resistance | Moderate | Strong (memory-hard) |
| Password length limit | 72 bytes | None |
| Tuning parameters | Rounds | Memory, time, parallelism |
| Library support | Everywhere | Most modern languages |
| OWASP recommendation | Acceptable | Preferred |
Which Should You Choose?
Choose Argon2id if you're starting a new project. It's the OWASP-recommended default and provides stronger resistance against modern hardware attacks. Use at least 64 MB of memory, 3 iterations, and 4 threads.
Bcrypt is still fine for existing systems. With 12+ rounds, it remains secure against remote attacks. If you're already using bcrypt and your hashes are salted, there's no urgent need to migrate — but consider upgrading new hashes to Argon2id over time.
Migrating gradually: You can verify passwords against old bcrypt hashes and re-hash them with Argon2id on the next successful login. The Auth Toolkit's Verify Password endpoint detects the algorithm automatically, and the needs_rehash field tells you when it's time to upgrade.
Verifying Passwords
Regardless of algorithm, always verify using constant-time comparison:
curl -X POST https://auth.toolkitapi.io/v1/auth/verify-password \
-H "X-API-Key: YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"password": "hunter2", "hash": "$2b$12$LJ3m4ys..."}'
The response tells you whether the password matches, which algorithm was detected, and whether the hash should be upgraded:
{
"valid": true,
"algorithm_detected": "bcrypt",
"needs_rehash": false
}