ProtemKey

How passwords are attacked · 5 min read

Password hashing explained: salts and cost factors

Password hashing turns a password into a stored value that is cheap to check once and expensive to check billions of times. Salts and cost factors are how.

A site that lets you log in has to check your password without keeping a copy of it. The tool for that is a password hash: a function that turns the password into a fixed-length value which can be recomputed and compared, but not run backwards. Every serious standard on the subject agrees on three ingredients: a hash function designed for passwords, a unique salt, and a tunable cost.

NIST SP 800-63B-4 puts it in one sentence: “Password hashing schemes take a password, a salt, and a cost factor as inputs and generate a password hash.”

Why an ordinary hash is not enough

General-purpose hash functions such as SHA-256 are built to be fast. That is a virtue for checking file integrity and a problem for passwords. If a stored password hash leaks, the attacker recovers passwords by guessing: hash a candidate, compare, repeat. A fast function lets them repeat that an enormous number of times per second. The OWASP Password Storage Cheat Sheet says fast algorithms such as SHA-256 “are not suitable for password storage because they allow attackers to perform large numbers of guesses quickly.”

So a password hash is deliberately slow and, in the modern designs, deliberately memory-hungry. Logging in costs the server one hash. Guessing costs the attacker one hash per guess, for every guess.

The salt

A salt is a random value generated for each stored password and kept next to the hash. The hash is computed over the password and the salt together.

It does three things:

  1. Identical passwords stop looking identical. Two accounts with the same password get different salts and so different hashes. An attacker cannot spot shared passwords by scanning for repeats.
  2. One guess no longer covers everyone. Without salts, an attacker hashes 123456 once and finds every account that used it. With salts, they must hash it again for each account. Work grows in direct proportion to the number of accounts.
  3. Precomputed tables stop working. Tables of hashes for millions of likely passwords (including the compressed form known as rainbow tables) can be built once and reused against any unsalted database. With a salt, a table would have to be built for every possible salt value.

NIST requires the salt to be “at least 32 bits in length.” There are 2^32 = 4,294,967,296 possible 32-bit salts, so a precomputed table would need to be repeated more than four billion times. RFC 9106 recommends 16 bytes (128 bits) of salt for Argon2.

A salt is not a secret. It is stored in the clear beside the hash, and that is fine; its job is uniqueness. A separate, secret value kept outside the database is called a pepper, and OWASP describes it as an optional extra layer.

The cost factor

The cost factor sets how much work one hash takes. NIST says it “SHOULD be as high as practical without negatively impacting verifier performance” and “SHOULD be increased over time to account for increases in computing performance.” OWASP’s general rule is that computing a hash “should take less than one second” on the server.

Because the cost is stored with each hash, a site can raise it later. A common approach, described by OWASP, is to rehash a password with the new setting the next time its owner logs in successfully.

The four schemes you will meet

Scheme What makes it expensive Main parameters
PBKDF2 Repeats an HMAC (for example HMAC-SHA-256) many times Iteration count
bcrypt A deliberately costly key setup, repeated 2^cost times Cost (log2 of the rounds)
scrypt Fills and reads a large block of memory in an order that is hard to shortcut N (memory/CPU cost), r (block size), p (parallelism)
Argon2 Fills memory in passes, designed to resist both GPU attacks and timing side channels m (memory in KiB), t (passes), p (lanes)

PBKDF2

PBKDF2 is defined in RFC 8018. It feeds the password and salt through a pseudorandom function, usually HMAC with a SHA-2 hash, and repeats it a set number of times. Each guess costs the attacker the same number of repetitions. Its weakness is that it needs very little memory, so specialized hardware can run many guesses side by side. OWASP recommends it where FIPS-140 validated implementations are required, with “a work factor of 600,000 or more” for HMAC-SHA-256.

bcrypt

bcrypt builds on an expensive key schedule and repeats it 2^cost times, so each step up in cost doubles the work. OWASP sets a minimum cost of 10 and now describes bcrypt as suited to legacy systems where Argon2 and scrypt are not available. It has a well-known quirk: most implementations only use the first 72 bytes of input, which matters for long passphrases and is one reason sites set password length limits.

scrypt

scrypt adds memory cost. Computing it requires holding a large table in memory, which makes cheap parallel hardware far less effective, because memory is the expensive part of a chip. OWASP’s minimum is N = 2^17, r = 8, p = 1.

Argon2

Argon2 won the Password Hashing Competition in 2015 and is specified in RFC 9106. It comes in three variants. RFC 9106 calls Argon2id the “primary variant”: it “works as Argon2i for the first half of the first pass over the memory and as Argon2d for the rest,” giving resistance to side-channel timing attacks and to memory trade-off attacks. OWASP’s minimum configuration is 19 MiB of memory, 2 passes and 1 lane.

Worked example: what slowness is worth

The site’s illustrative rates assume about 10^11 guesses per second against a fast unsalted hash and 10,000 per second against a slow, bcrypt-class hash:

Illustrative attack rates used throughout this site. They are order-of-magnitude assumptions, not measurements; real rates vary with hardware, hash settings and defenses.
ScenarioRate (assumed)Situation
Online, rate-limited100 guesses per houra login form that slows down or locks out repeated failures
Offline, slow hash10,000 guesses per seconda stolen database hashed with a deliberately slow, salted scheme (bcrypt class)
Offline, fast hash100 billion guesses per seconda stolen database hashed with a fast unsalted hash (MD5, SHA-1 or NTLM class) on a high-end GPU rig

The ratio between them is 10^11 ÷ 10^4 = 10^7. In bits, log2(10^7) = 23.25. A slow hash therefore buys roughly the same protection as adding about 23 bits to every password: a little less than two extra random EFF words (2 × 12.925 = 25.85 bits).

Two more pieces of arithmetic:

  • Raising bcrypt’s cost from 10 to 12 multiplies the work per hash by 2^12 ÷ 2^10 = 4, i.e. adds 2 bits.
  • PBKDF2 at 600,000 iterations costs about log2(600,000) = 19.19 bits more per guess than a single iteration of the same function.

Take an 8-character password drawn at random from all 94 printable characters (52.44 bits). Against the fast unsalted hash, the average time to find it is about 8.5 hours. Against the slow hash, it is about 9,700 years. Same password, same attacker; only the storage changed.

Note: Slowness multiplies the cost of every guess equally. It does nothing for a password that is near the top of an attacker’s list. password is still guessed first, just a few milliseconds later.

Summary

  • A password hash must be slow, salted and tunable; general-purpose fast hashes are the wrong tool.
  • The salt is unique per password and not secret. It stops precomputation and forces per-account work.
  • The cost factor sets the price of one guess, and it should rise as hardware gets faster.
  • Argon2id, scrypt, bcrypt and PBKDF2 differ mainly in whether they also make the attacker pay in memory.
  • Good hashing slows attackers down by many orders of magnitude, but a common password is still guessed early.

Sources

  1. NIST SP 800-63B-4, Digital Identity Guidelines: Authentication and Authenticator Management (2025)
  2. OWASP Password Storage Cheat Sheet
  3. RFC 9106: Argon2 Memory-Hard Function for Password Hashing and Proof-of-Work Applications (2021)
  4. RFC 8018: PKCS #5: Password-Based Cryptography Specification Version 2.1 (2017)

General explanation, not advice for a particular situation. Updated 2026-10-31.