BackendBench
Coffee

Unravel โ€บ Java concepts โ€บ Security

Spring

Password hashing (BCrypt, Argon2, PBKDF2)

Passwords must be stored as slow, salted hashes so that a stolen database does not reveal them.

๐Ÿ”Ž Unravel this example

At a glance

Mental model

A blender that makes a smoothie from your password and a random pinch of salt.

What happens, in order
  1. 1passwordEncoder.encode(raw) returns a string containing the algorithm, cost, a random salt and the hash ($2a$10$...)Store that whole string.
  2. 2passwordEncoder.matches(raw, stored) re-hashes the attempt with the salt and cost read from the stored string and compares in constant timeYou never decrypt a password.
  3. 3Cost is a parameter: BCrypt strength 10 to 12 (about 100 ms), Argon2 with memory and iterations, PBKDF2 with 600,000+ iterationsRaise it over time as hardware improves.
  4. 4DelegatingPasswordEncoder stores a prefix ({bcrypt}) so you can migrate to a new algorithm and re-hash users on their next login (upgradeEncoding)
Remember
Watch out
Using SHA-256 or MD5 for passwordsStoring passwords reversiblyCost never raisedComparing hashes with equals instead of the encoder
Example (highlighted lines explain it)
PasswordEncoder encoder = new BCryptPasswordEncoder(12);String stored = encoder.encode(rawPassword);boolean ok = encoder.matches(attempt, stored);if (ok && encoder.upgradeEncoding(stored)) {    user.setPasswordHash(encoder.encode(attempt));}
  1. line 1 uses BCryptPasswordEncoder, the core of Password hashing (BCrypt, Argon2, PBKDF2)
  2. line 5 uses upgradeEncoding, the core of Password hashing (BCrypt, Argon2, PBKDF2)
Read the full explanation (about 2 min)

Passwords must be stored as slow, salted hashes so that a stolen database does not reveal them. BCrypt, Argon2 and PBKDF2 are designed for this; plain SHA-256 or MD5 are not.

A mental model

A blender that makes a smoothie from your password and a random pinch of salt. Anyone can check that a password makes the same smoothie, but nobody can turn the smoothie back into fruit, and the slow blender makes guessing billions of passwords impractical.

How it works

passwordEncoder.encode(raw) returns a string containing the algorithm, cost, a random salt and the hash ($2a$10$...). Store that whole string.

passwordEncoder.matches(raw, stored) re-hashes the attempt with the salt and cost read from the stored string and compares in constant time. You never decrypt a password.

Cost is a parameter: BCrypt strength 10 to 12 (about 100 ms), Argon2 with memory and iterations, PBKDF2 with 600,000+ iterations. Raise it over time as hardware improves.

DelegatingPasswordEncoder stores a prefix ({bcrypt}) so you can migrate to a new algorithm and re-hash users on their next login (upgradeEncoding).

Going deeper

A fast hash (SHA-256, MD5, even salted) lets attackers try billions of guesses per second on GPUs. The slowness and memory use of password hashes is the feature.

NoOpPasswordEncoder and StandardPasswordEncoder are deprecated for good reasons: they store plain text or weak digests, and must never appear in production.

BCrypt only reads the first 72 bytes of the password. Do not pre-hash with a raw hash (NUL bytes), and consider a pepper kept in a secret store or hardware module for defence in depth.

Under the hood

BCrypt runs the Blowfish key schedule 2^cost times with the salt and password, which is deliberately expensive and hard to parallelise on GPUs. Argon2 additionally needs a configurable amount of memory, defeating specialised hardware. The 128-bit salt is generated with SecureRandom per password so equal passwords give different hashes.

Common mistakes

  • Using SHA-256 or MD5 for passwords.
  • Storing passwords reversibly.
  • Cost never raised.
  • Comparing hashes with equals instead of the encoder.

How Unravel recognises it

When you paste code, Unravel looks for these signs of password hashing (bcrypt, argon2, pbkdf2). Spotting them yourself is a good habit when you read unfamiliar code:

Part of a learning path

Related concepts

Frequently asked questions

What is Password hashing (BCrypt, Argon2, PBKDF2)?

Passwords must be stored as slow, salted hashes so that a stolen database does not reveal them. BCrypt, Argon2 and PBKDF2 are designed for this; plain SHA-256 or MD5 are not.

What are the most common mistakes with Password hashing (BCrypt, Argon2, PBKDF2)?

Using SHA-256 or MD5 for passwords. Storing passwords reversibly. Cost never raised. Comparing hashes with equals instead of the encoder.

How can I see Password hashing (BCrypt, Argon2, PBKDF2) in real code?

Paste your own Java into Unravel and it will point out where password hashing (bcrypt, argon2, pbkdf2) appears, explain how it works at the detail level you choose, and draw the flow of the code around it.