Password security is one of those topics where the gap between “good enough” and “actually secure” is enormous. Developers who store passwords in plaintext know they are doing it wrong. But many developers who hash passwords are still doing it wrong — using fast hashes, skipping salts, or implementing policies that make passwords weaker, not stronger.

This guide covers how password security works in 2026, from the fundamentals of hashing to practical policy decisions.

Why Hashing Matters

When a user creates an account, you need to verify their password on future logins. The naive approach — storing the password directly — is catastrophic when your database is breached. And databases do get breached, regularly.

Hashing converts a password into a fixed-length string that cannot be reversed. When the user logs in, you hash their input and compare it to the stored hash. You never need to know the original password.

"mypassword123" → $2b$12$LJ3m4ys3Lg...  (bcrypt hash)

Even if an attacker steals every hash in your database, they cannot directly recover passwords. They would have to try guessing passwords and comparing hashes — which is where the choice of algorithm becomes critical.

Not All Hash Functions Are Equal

Cryptographic Hashes (SHA-256, MD5)

General-purpose cryptographic hashes like SHA-256 and MD5 are designed to be fast. A modern GPU can compute billions of SHA-256 hashes per second. This speed is a feature for their intended purposes (data integrity, digital signatures) but a disaster for password storage.

An attacker with a GPU can try every possible 8-character password in hours:

MD5:     ~40 billion hashes/sec per GPU
SHA-256: ~10 billion hashes/sec per GPU

Never use MD5, SHA-1, or SHA-256 alone for password hashing. They are not designed for this purpose.

Password-Specific Hashes

Password hashing algorithms are deliberately slow. They are designed to make brute-force attacks expensive:

bcrypt — The workhorse of password hashing since 1999. It uses a configurable work factor that controls how many iterations the hash performs:

Work factor 10: ~100 ms per hash
Work factor 12: ~400 ms per hash
Work factor 14: ~1.6 sec per hash

At 400ms per hash, an attacker trying 1 billion passwords needs over 12 years — versus seconds with SHA-256.

$2b$12$LJ3m4ys3LgHJK8vQp5N6KeVZ2W1x9mC7TdOqKp6sY.BxCm2W5Hq
 │  │  │                                                    │
 │  │  └── 22-char salt (Base64)                            │
 │  └───── work factor (12)                                 │
 └──────── algorithm version (2b)                           │
           └── 31-char hash ────────────────────────────────┘

Argon2 — Winner of the Password Hashing Competition (2015). It adds memory hardness, making GPU attacks more difficult:

Argon2id — recommended variant (combines Argon2i and Argon2d)

Parameters:
  - Time cost: iterations (higher = slower)
  - Memory cost: KB of memory required
  - Parallelism: number of threads

GPUs have many cores but limited per-core memory. Argon2’s memory requirement forces each hashing attempt to use significant RAM, dramatically reducing how many parallel attempts a GPU can make.

scrypt — Another memory-hard function, used by some cryptocurrency systems. Less common than Argon2 for new applications but still solid.

Which to Choose?

  • New projects: Argon2id with recommended parameters (19 MiB memory, 2 iterations, 1 thread)
  • Existing projects: bcrypt with work factor 12+ is still excellent
  • Avoid: MD5, SHA-1, SHA-256, SHA-512 (without KDF), PBKDF2 with low iterations

Salting: Why It Is Non-Negotiable

A salt is a random value added to each password before hashing:

hash("mypassword" + "x7Kp2mN9qR")  →  unique hash
hash("mypassword" + "bT4wL1fA3s")  →  different hash

Without salts, identical passwords produce identical hashes. An attacker can precompute hashes for common passwords (a rainbow table) and look up matches instantly. With unique salts, even the same password produces different hashes for different users, making rainbow tables useless.

bcrypt and Argon2 generate and embed salts automatically. If you are using these algorithms through standard libraries, salting is handled for you. If you are implementing something custom, you are probably making a mistake — use the standard library.

Rainbow Tables and Dictionary Attacks

Rainbow Tables

A rainbow table is a precomputed mapping from hash to password. For unsalted MD5, tables covering all common passwords are freely available online. Salting defeats rainbow tables because the attacker would need to build a separate table for every possible salt.

Dictionary Attacks

Attackers start with lists of known passwords (from previous breaches) and common patterns. The top passwords are depressingly predictable:

123456
password
123456789
qwerty
password123

No amount of hashing protects a user who chooses “password123”. This is where password policies come in.

Credential Stuffing

Attackers take username/password pairs from one breach and try them on other services. Since most people reuse passwords, this is devastatingly effective. The defense is not better hashing — it is multi-factor authentication and breach detection.

Password Policy Best Practices

NIST Special Publication 800-63B (updated 2024) provides evidence-based password guidelines:

Do

  • Require a minimum of 8 characters (NIST recommends at least 8, many organizations require 12+)
  • Allow long passwords — up to 64 characters or more
  • Allow all printable characters including spaces and Unicode
  • Check against breached password lists — reject passwords found in known breaches
  • Implement rate limiting on login attempts
  • Require multi-factor authentication for sensitive accounts
  • Allow paste in password fields — this encourages password manager use

Don’t

  • Don’t require periodic password changes — this leads to weaker passwords (users append “1”, “2”, “3”)
  • Don’t require specific character classes — “must contain uppercase, lowercase, digit, and special character” leads to “Password1!” patterns
  • Don’t use security questions — they are easily researched or guessed
  • Don’t truncate passwords — if a user enters a 50-character password, hash all 50 characters
  • Don’t limit character types — “no special characters allowed” is a red flag

The shift in thinking is significant: encourage longer, memorable passwords over shorter, complex ones. “correct horse battery staple” is far stronger than “P@ssw0rd!” and much easier to remember.

Implementation Checklist

  1. Use bcrypt (work factor 12+) or Argon2id
  2. Salts are handled automatically by these algorithms — do not roll your own
  3. Check passwords against the Have I Been Pwned API (k-anonymity model protects privacy)
  4. Enforce minimum length (12+ characters recommended)
  5. Implement account lockout or exponential backoff after failed attempts
  6. Support and encourage multi-factor authentication
  7. Hash on the server, not the client (client-side hashing can be bypassed)
  8. Log authentication failures for security monitoring

Useful Tools

When building authentication systems, you often need to generate test hashes, verify bcrypt outputs, or check password strength during development.

Our Password Generator creates cryptographically random passwords of any length and character composition. The Bcrypt Generator lets you hash passwords with configurable work factors and verify passwords against existing hashes — useful for testing your authentication flow. And the Password Strength Checker evaluates passwords against multiple criteria including length, entropy, common patterns, and breach databases.

All tools run entirely in your browser — passwords are never sent to any server.

The Bottom Line

Password security is not about implementing one clever technique — it is about layering defenses correctly. Use a proper password hashing algorithm, enforce sensible policies, protect against brute force, and add multi-factor authentication. Each layer compensates for weaknesses in the others.

The attacker only needs one of your defenses to fail. Make sure none of them do.