A service needs to verify a password, not recover it. The stored record should make each offline guess expensive after a database leak and prevent equal passwords from sharing one verifier. Plaintext, reversible encryption, and fast general-purpose hashes fail that boundary.

Store a Self-Describing Verifier

OWASP currently recommends Argon2id with a minimum configuration of m=19 MiB, t=2, and p=1. That is a floor, not a universal production setting. The production setting comes from benchmarking the library on the authentication tier: it is the highest cost the service can sustain under bounded concurrency, and its parameters are recorded with each verifier. Scrypt is the next choice when Argon2id is unavailable. PBKDF2-HMAC-SHA-256 fits environments that require a validated FIPS implementation. Bcrypt is mainly a compatibility choice for existing systems and has a 72-byte input limit in most implementations.

Each password has one stored record:

$argon2id$v=19$m=19456,t=2,p=1$<unique-salt>$<derived-output>

The algorithm, version, cost parameters, and salt are public verifier metadata. Keeping them with the derived output makes the record self-describing and allows later migration. A maintained password-hashing library should generate the salt, encode the record, and verify it. A hand-built hash(password + salt) construction lacks the work and memory controls of a password KDF.

Verification and Migration

  1. The verifier parses the stored algorithm, parameters, salt, and expected output.
  2. It applies the same KDF to the candidate password.
  3. The library’s constant-time verifier compares the outputs.
  4. After a successful login, the service rehashes the password when the algorithm or cost is below current policy.

Rehash-on-login makes migration incremental. An account using an older algorithm can move after a successful verification, when plaintext is briefly available in memory. Accounts that do not return need an expiry or reset policy. Otherwise the oldest verifier remains indefinitely.

An optional pepper adds a secret key outside the password table. It can make a database-only theft insufficient for testing guesses, but it introduces storage, availability, and rotation work. A compromised pepper usually cannot be replaced for existing records without the user’s password or a forced reset. It does not repair a weak KDF.

Failure and Breach Paths

  • Online verification is rate-limited independently of the KDF. Expensive hashing raises offline cost. It does not stop credential stuffing against the live endpoint.
  • Concurrent KDF work is bounded so an attacker cannot turn the intended memory cost into service exhaustion.
  • Passwords and candidate bytes stay out of logs, traces, crash reports, and exception messages. Verifier records deserve the same access controls as the password table.
  • After a verifier leak, the response preserves evidence and assesses the cracking window from measured KDF cost and password quality. Credentials and sessions are reset according to that risk, a compromised pepper is rotated, and the applicable notification process is followed.

A stolen verifier table gives an attacker an offline test with no service-side rate limit. Measured KDF cost, password quality, and any separately protected pepper determine the remaining work factor.

Questions

References