Skip to content
security

Password Security and Storage: Hashing, Salting, and What Actually Works in 2026

Updated 2026-05-15 · 11 min read · By @guptadeepak

Key takeaways

  • Argon2id is the 2026 default password-hashing algorithm. bcrypt is acceptable; scrypt is fine; PBKDF2 only when FIPS requires it; SHA-256 alone is unacceptable.
  • Always salt per user (uniqueness, not secrecy) and always use a slow KDF. Either alone is insufficient.
  • Pepper is defense-in-depth — useful when the application secret store is judged less leak-prone than the credential database; not a substitute for proper hashing.
  • Never encrypt passwords. Encryption is reversible; hashing is not. A breach of an 'encrypted password' database returns every password to plaintext.
  • The fastest path past the password problem is to stop having passwords. Passkeys are the 2026 production-grade replacement.

The threat model

Three attacks define the design constraints:

  • Rainbow tables: precomputed lookup tables that reverse hashes by lookup. Defeated by salting (per-user salts make precomputation per-user).
  • Brute force: exhaustive search over candidate passwords. Defeated by slow KDFs (Argon2id, bcrypt, scrypt deliberately slow each guess to 50-500ms).
  • Database breach plus offline cracking: the attacker steals the database and runs unconstrained guesses on stolen hardware. Defeated by the combination of slow KDF, per-user salts, and (optionally) an application-layer pepper.

The historical breaches that taught these lessons — LinkedIn 2012 (unsalted SHA-1), Adobe 2013 (reversible encryption with ECB-mode AES), Yahoo 2013 (MD5), and many more — are all post-mortems of skipping one of these three defenses. Modern password storage is the result of two decades of failure analysis; the patterns work.

Hashing: pick a slow KDF

The 2026 acceptable set:

AlgorithmWhen to pickWhy
Argon2idNew deployments, no FIPS constraintPHC winner (2015), RFC 9106, memory-hard, configurable resistance to GPU and side-channel attacks
bcryptLegacy compatibility, operational familiarity1999 design, extremely well-tested, CPU-only cost
scryptArgon2 unavailable, memory-hard requiredRFC 7914, memory-hard, slightly older design than Argon2
PBKDF2-SHA256, 600K+ itersFIPS-compliant environmentsNIST-approved, iteration-count tunable, not memory-hard

The unacceptable set in 2026:

  • SHA-256 alone, MD5, SHA-1: too fast. GPU cracking trivial.
  • PBKDF2 with low iteration counts (older OWASP recommended 1000+; 2026 baseline is 600,000+ for PBKDF2-SHA256, 210,000+ for SHA-512).
  • Custom-rolled hashing schemes: every "we invented our own hashing" turns into a CVE.
  • Reversible encryption: see below.

Salting: per user, random, public

A salt is per-user random data mixed into the password before hashing. Properties:

  • Uniqueness, not secrecy: stored alongside the hash in the database; security comes from being unique per user, not from being hidden.
  • Length: 16 bytes (128 bits) is standard. Generated from a CSPRNG.
  • Automatic in modern libraries: Argon2id, bcrypt, and scrypt generate and embed salts in the hash output through the standard library APIs. Hand-writing salt generation is a smell — use the library.

What goes wrong:

  • Static salt across users: defeats the entire point. The attacker computes one rainbow table for the static salt and cracks the whole database.
  • Reusing salts across rotations: if you rotate hashing parameters (Argon2 work factor, for instance), the rehash should also re-generate the salt.
  • Hand-rolling salt storage: store the salt as the library outputs it (typically embedded in the hash string). Don't put the salt in a separate column and lose the binding.

Pepper: optional defense-in-depth

A pepper is a single secret (or small rotating set) mixed into every password hash, stored in application configuration / secrets manager / HSM — outside the credential database.

The defense: if the database leaks but the pepper does not, the attacker cannot compute hashes for guessed passwords. Even Argon2id with a valid salt is useless to the attacker without the pepper.

When pepper is worth it:

  • The database leaks more easily than the application secret store (database replicates to backups, analytics warehouses, staging environments; the pepper lives in a centralized HSM or secrets manager).
  • The compliance regime requires layered defenses (some interpretations of PCI DSS 4.0 and HIPAA Security Rule favor pepper).

When pepper is not worth it:

  • The application secret and the database live in the same trust boundary (same cloud account, same database, same access pattern). Then the pepper leaks with the database and adds operational complexity without security.
  • Greenfield team without strong secret-management discipline. The operational complexity of pepper rotation (every existing hash needs to be re-peppered, which means a versioning column, gradual rehash-at-login flow, monitoring) usually outweighs the marginal security benefit.

The 2026 honest take: pepper is reasonable defense-in-depth for organizations with strong key management practices. It is not a substitute for proper hashing, and engineering attention is better spent moving toward passkeys.

Never encrypt passwords

A recurring architectural error worth flagging explicitly: storing passwords with reversible encryption (AES, ChaCha20, RSA) is wrong. The reasoning:

  • Encryption is reversible by design. The whole point of the operation is that anyone with the key gets the plaintext back.
  • Key compromise = total compromise. An attacker who gets the encryption key (which lives wherever your other keys live — often the same blast radius as the database) instantly returns every stored password to plaintext.
  • Hashing is one-way. Even with full database access plus full code access, the attacker has to crack each hash individually.

Production CIAM never encrypts user passwords. If a system advertises "encrypted password storage" it is either using the wrong vocabulary (and means hashing) or wrong technically. Either way, ask the question.

The fastest path past the problem

Passwords are an inherited primitive that no longer fits modern threat models. They are phishable, reusable across sites, forgettable, and the source of most account takeovers. The 2026 production-grade answer is to stop having them.

Passkeys (covered in detail) are the modern replacement: phishing-resistant (the credential is scoped to the relying party domain), user-friendly (no remembering anything, biometric unlock), and broadly supported (Apple, Google, Microsoft, every major browser, most major SaaS in 2026). The migration path is gradual: offer passkey enrollment to new users, prompt existing users to enroll at next login, retire password-only flows over 6-18 months.

Until the migration is complete, the existing password store has to be correct: Argon2id with per-user salts, optional pepper, no encryption. The work to get the storage right is a one-time engineering investment; the recurring work to evolve past passwords is the strategic move.

Implementation guidance

  1. Pick Argon2id for greenfield (memory cost 64 MiB, time cost 3, parallelism 4 is a reasonable starting point — tune to your server's tolerance).
  2. For legacy bcrypt deployments: cost factor 12+ is the 2026 baseline. Migration to Argon2id is rehash-at-next-login.
  3. For PBKDF2 (FIPS environments): 600,000 iterations of SHA-256 minimum.
  4. Always per-user salt, library-generated. Never hand-rolled, never reused.
  5. Pepper if your secret-management discipline supports it. Skip otherwise.
  6. Never encrypt passwords. Hash them.
  7. Log authentication events (success and failure) with enough structure to detect credential-stuffing attacks. Stream to SIEM.
  8. Stage the passkey migration, in parallel with all of the above. The endgame is no passwords.

Related vendors

FAQ

Why is SHA-256 not enough to store passwords?
SHA-256 is fast — billions of guesses per second on commodity GPUs. The point of a password-hashing function is to be slow enough that exhaustive brute-force search is economically infeasible. Argon2id, bcrypt, and scrypt are deliberately 1000-100,000× slower per hash than SHA-256, which collapses the per-guess economics. Using a general-purpose fast hash for password storage is a category error: same shape, wrong tool.
What's the difference between salting and hashing?
Hashing is the one-way transformation of the password into a fixed-length digest. Salting is mixing per-user random data into the input before hashing, so identical passwords produce different hashes. Both are required — hashing alone is vulnerable to rainbow tables; salting alone is plaintext. The modern KDFs (Argon2id, bcrypt, scrypt) handle salt generation and storage automatically through library defaults.
Should I use Argon2, bcrypt, or scrypt?
Argon2id (RFC 9106) for new deployments. bcrypt remains acceptable and is the safe choice when library availability or operational familiarity matters. scrypt (RFC 7914) is comparable to Argon2id and a reasonable alternative when Argon2 libraries are unavailable. PBKDF2 with 600K+ SHA-256 iterations is the FIPS-compliant option for environments requiring NIST-certified algorithms. All four are acceptable; pick one, tune it properly, and don't change without reason.
Do I need a pepper if I'm using Argon2 with a salt?
Not strictly. Pepper is a defense-in-depth layer that helps when the credential database is judged more leak-prone than the application secret store. If your database leaks but your pepper stays in your secrets manager, even an attacker with the full database and algorithm cannot crack passwords without first finding the pepper. The cost is operational complexity around pepper rotation. If your secrets manager and database share the same trust boundary, pepper buys you little. The 2026 honest take: focus engineering effort on moving to passkeys; if you must do passwords, use Argon2id with proper salts, and add pepper if your threat model justifies it.
Should I encrypt passwords instead of hashing them?
No. Encryption is reversible by design — anyone with the encryption key can decrypt every stored password back to plaintext. Hashing is one-way; even with full database access an attacker has to crack each hash individually. 'We encrypt passwords' is a red flag in any architecture review; the correct answer is 'we hash passwords with Argon2id and per-user salts.'
What length should the salt be?
16 bytes (128 bits) is the standard, generated from a cryptographically secure random number generator. Longer is fine; shorter starts to risk salt collisions at scale. The modern KDFs (Argon2id, bcrypt, scrypt) generate appropriate salts automatically through library defaults — write your own salt-generation code only if you have a specific reason.

Sources

  • RFC 9106 — Argon2 Memory-Hard Function for Password Hashing
  • RFC 7914 — The scrypt Password-Based Key Derivation Function
  • RFC 8018 — PKCS #5: Password-Based Cryptography Specification v2.1
  • Provos and Mazières — A Future-Adaptable Password Scheme (USENIX 1999, the bcrypt paper)
  • OWASP Password Storage Cheat Sheet
  • NIST SP 800-63B-4 — Digital Identity Guidelines: Authentication and Authenticator Management (2024)
Last reviewed 2026-05-15.