Articles

Password Hashing Explained: Salting, Bcrypt, and Argon2

Password hashing turns a password into a one-way, salted digest so a stolen database doesn't hand over credentials. How bcrypt and Argon2 work.

Chisato Chisato · · 4 min read
A padlock icon glowing red over a keyboard

Password hashing is the practice of running a user’s password through a deliberately slow, one-way function before storing it, so that even if an attacker steals the database, they get a field of unusable digests instead of usable credentials. Done right, it turns a catastrophic breach into an expensive, time-consuming cracking problem instead of an instant credential leak.

Why hashing, not encryption

Passwords should never be stored in plaintext, and they shouldn’t be encrypted either — see hashing vs encryption for the full distinction. Encryption is reversible by design: whoever holds the key can recover the original password, which means the server (or anyone who compromises it) can too. Hashing is one-way. A well-designed password hash function takes the password and produces a fixed-length digest that cannot practically be reversed back into the original text. Verification works by hashing the login attempt and comparing digests, never by decrypting anything.

The problem with a plain hash

A fast general-purpose hash like SHA-256 is a poor choice for passwords, even though it’s a fine choice for things like file integrity checks. Two problems stand out:

  • Speed is the enemy. SHA-256 is designed to be fast — billions of hashes per second on commodity hardware. That’s great for checksums and terrible for passwords, because it means an attacker with a stolen hash database can try billions of guesses per second per GPU.
  • Identical passwords produce identical hashes. Without anything else added, two users who both pick “password123” get byte-identical hash values. An attacker can precompute a rainbow table — a lookup of hash values for common passwords — and reverse huge swaths of a stolen database in one pass, without cracking anything individually.

Salting: defeating precomputation

A salt is a random value generated per password and stored alongside the hash (it doesn’t need to be secret). The salt is concatenated with the password before hashing, so hash(password + salt) differs for every user even when the underlying password is identical. This defeats rainbow tables outright — an attacker would need a separate precomputed table per salt, which is infeasible at scale — and it means cracking one password reveals nothing about any other, even identical, password in the database. Every modern password hashing library generates and stores the salt automatically; you should never need to manage it by hand.

Purpose-built slow hashes: bcrypt, scrypt, Argon2

Salting alone doesn’t fix the speed problem, so password hashing relies on algorithms designed to be deliberately, tunably slow:

  • bcrypt (1999) wraps the Blowfish cipher in a structure with a configurable cost factor that exponentially increases the number of internal rounds. It has decades of production hardening behind it and remains a safe, conservative default.
  • scrypt (2009) adds a memory-hardness requirement on top of CPU cost, making it expensive to parallelize on GPUs and ASICs, which are cheap at raw compute but comparatively starved for memory bandwidth.
  • Argon2 (2015) won the Password Hashing Competition and is the current recommended default for new systems. It comes in three variants — Argon2d (maximizes resistance to GPU cracking), Argon2i (resists side-channel timing attacks), and Argon2id (a hybrid, and the generally recommended choice) — and exposes independent time, memory, and parallelism cost parameters.

Comparing the options

bcryptscryptArgon2
Released199920092015
Memory-hardNoYesYes
Tunable parametersCost factor onlyCost, memory, parallelismTime, memory, parallelism
GPU/ASIC resistanceModerateStrongStrongest
Track recordVery longLongGrowing
Recommended for new systemsAcceptableAcceptablePreferred

All three beat a plain fast hash by orders of magnitude. If you’re maintaining a legacy system already on bcrypt, migrating isn’t urgent; if you’re starting fresh, Argon2id is the better default.

Tuning the cost factor

Every one of these algorithms exposes a work factor, and it should be tuned to your hardware, not left at a library default forever. The general approach: pick a cost parameter that takes somewhere around a few hundred milliseconds to hash on your production servers. That’s imperceptible to a real login flow but expensive to brute-force at scale, since the attacker pays that same cost per guess. As hardware gets faster, the cost factor should periodically go up — most libraries support re-hashing on a user’s next successful login when the stored parameters are below the current target, so you can ratchet up cost without forcing a mass password reset.

Hashing fits into a larger auth picture

Password hashing protects one specific failure mode: a stolen database. It doesn’t protect against phishing, credential stuffing across sites, or a compromised client. That’s why it’s usually paired with multi-factor authentication, rate-limited login endpoints, and increasingly with passkeys, which sidestep the password entirely in favor of public-key cryptography. If your application issues session tokens after login, see what a JWT is for how that credential is typically structured once the user is authenticated.

The takeaway

Password hashing is deliberately slow and deliberately salted: the salt defeats precomputed rainbow tables, and the slowness makes brute-forcing expensive even after a breach. Use a purpose-built algorithm — bcrypt, scrypt, or preferably Argon2id — never a fast general-purpose hash like SHA-256 on its own, and tune the cost parameter to your hardware rather than trusting a default forever. It’s one layer in a defense that should also include MFA and rate limiting, but it’s the layer that decides whether a stolen database is a minor incident or a catastrophe.

Chisato Chisato · · 5 min read

What Is Session Fixation?

Session fixation tricks a victim into using an attacker-known session ID, so logging in hands the attacker an authenticated session too.

#Security #Authentication #Web Development
Chisato Chisato · · 4 min read

The OAuth PKCE Flow Explained

PKCE hardens the OAuth authorization code flow against interception, and is now recommended for every client type, not just mobile and single-page apps.

#Security #Authentication #Web Development