Password Hashing: How to Store Passwords Securely

Leader 1 2 13
calendar_today agoschedule2 min read

Password storage looks simple at first:

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

But a production-ready password-storage system involves more than calling a hashing function.

Why You Should Never Store Passwords Directly

Never store:

email: *Emails are not allowed*
password: mypassword123

If your database is compromised, the attacker immediately has the user's password.

This is especially dangerous because people often reuse passwords across multiple services.

Instead, store a password hash:

password
   ↓
password hashing
   ↓
password_hash
   ↓
database

The goal is to make recovering the original password from a stolen database expensive.

Hashing vs Encryption

Encryption is reversible:

plaintext → encryption → ciphertext → decryption → plaintext

Hashing is designed to be one-way:

password → hash → password_hash

Your application doesn't need to recover the original password. It only needs to verify whether a login attempt is correct.

That's why passwords should normally be hashed rather than encrypted.

Why Not SHA-256?

SHA-256 is a secure cryptographic hash function, but it is designed to be fast.

That's useful for many applications, but not for passwords.

An attacker with a stolen database can repeatedly guess passwords and calculate their hashes.

For password storage, we want the opposite: hashing should deliberately be expensive.

Password-specific algorithms include:

  • bcrypt
  • scrypt
  • PBKDF2
  • Argon2

For new applications, Argon2id is a strong modern choice.

Salt

A salt is a unique random value generated for each password.

Without salts:

User A: password123 → same hash
User B: password123 → same hash

With unique salts:

User A: password123 + salt A → hash A
User B: password123 + salt B → hash B

The salt doesn't need to be secret. It is normally stored alongside the password hash.

Salts make large-scale precomputed attacks much less useful.

Pepper

A pepper is a secret value kept outside the password database.

A simplified design can look like:

password
   ↓
pepper
   ↓
Argon2id + unique salt
   ↓
password hash

Unlike a salt, the pepper must remain secret.

It should be stored using proper secret-management infrastructure rather than in the same database as the password hashes.

Argon2id

Argon2id is designed specifically for password hashing and can be configured to consume both CPU time and memory.

That memory requirement matters because attackers often use GPUs and other hardware to perform large numbers of password guesses.

The important parameters include:

  • Memory cost — how much memory hashing requires
  • Time cost — how much computation is performed
  • Parallelism — how much parallel processing is used

Don't blindly copy parameters from another application. Benchmark them on your own production-like infrastructure.

Rehashing

Security requirements change.

You might start with one set of Argon2id parameters and increase them later.

You don't need to force every user to reset their password.

Instead:

Login
  ↓
Verify password
  ↓
needsRehash?
  ↓
Yes → hash again with new parameters
  ↓
Save new hash

This gradually upgrades your password database as users log in.

Production Checklist

Before shipping password authentication, make sure you:

  • Use a dedicated password-hashing algorithm
  • Prefer Argon2id for new systems
  • Generate a unique random salt per password
  • Keep any pepper outside the database
  • Benchmark your hashing parameters
  • Support hash upgrades
  • Rate-limit authentication endpoints
  • Avoid leaking whether an account exists
  • Never log passwords, hashes, or peppers
  • Test both successful and failed authentication paths

Password hashing solves one specific problem:

How do we make a stolen password database much harder to turn into usable passwords?

For a deeper explanation of the concepts and implementation details, read the full guide:

Password Hashing: How to Store Passwords Securely

2 Comments

2 votes
2
🔥 Join developers growing publicly
Share your knowledge, build in public, and grow your developer presence with a global community.

More Posts

MCP Is the USB-C of AI. So Why Are You Plugging Everything In?

Ken W. Algerverified - Jun 10

I’m a Senior Dev and I’ve Forgotten How to Think Without a Prompt

Karol Modelski - Mar 19

Your Backup Data Knows More Than You Think. HYCU aiR Is Finally Asking It the Right Questions.

Tom Smithverified - May 14

Why Email-Only Contact Forms Are Failing in 2026 (And What Developers Should Do Instead)

JayCode - Mar 2

Master-Class: Scaling Databases with Sharding, Partitioning, and Consistent Hashing

piyush6348 - Apr 19
chevron_left
1.1k Points16 Badges
Pakistannoorulhassan.com
6Posts
3Comments
4Connections
I am a developer and product builder based in Jhang, Pakistan, focused on creating practical develop... Show more

Related Jobs

View all jobs →

Commenters (This Week)

3 comments
3 comments
2 comments

Contribute meaningful comments to climb the leaderboard and earn badges!