Safeguard
Security Guides•

How do you review a password hashing change in a pull request?

Check the algorithm, cost parameters, per password salt, pepper storage and bcrypt length limits against the OWASP Password Storage Cheat Sheet.

Safeguard Security Team
3 min read

Check four things: the algorithm, its cost parameters, a unique salt per password, and how existing hashes are migrated. OWASP's current order of preference is Argon2id, then scrypt, then bcrypt for legacy systems, with PBKDF2 where FIPS-140 compliance is required.

The recommended order

The OWASP Password Storage Cheat Sheet ranks the options as follows, with the minimums it states:

AlgorithmMinimum settings statedWhen to use
Argon2id19 MiB memory, 2 iterations, parallelism 1First choice
scryptN=2^17 (128 MiB), r=8, p=1When Argon2id is not available
bcryptWork factor of at least 10Legacy systems only
PBKDF2600,000 iterations with HMAC-SHA-256When FIPS-140 is required

The sheet lists equivalent memory and iteration trade-offs for Argon2id, so a different balance may be fine. Check the current page when you set values, since recommendations get updated.

Reviewer questions

  1. Which algorithm and parameters does the diff use? Compare against the table.
  2. Is the salt unique and random per password? Salts need not be secret, but they must be unique. Most common libraries generate and store them automatically, so a hand rolled salt is a flag.
  3. Is anything fast used for passwords, such as a plain hash function? That is a reason to block.
  4. Where is a pepper stored, if used? The sheet says it should live separately from the database, ideally in a vault or HSM, and it adds defense in depth but no security on its own.
  5. How do old hashes move to the new scheme? A common approach is to rehash at next login, but the plan should be written down.

bcrypt traps

The sheet notes bcrypt accepts a maximum of 72 bytes in most implementations, so enforce a 72 byte maximum password length or a lower limit your implementation has. It also warns that pre-hashing before bcrypt is risky because of null byte truncation and password shucking. If pre-hashing is unavoidable, it suggests HMAC-SHA-384 keyed with a pepper and base64 encoded, with the pepper stored outside the database.

# red flag: a general purpose hash, no salt, no work factor
digest = hashlib.sha256(password.encode()).hexdigest()

# better: a password hashing library with sensible defaults
hasher = argon2.PasswordHasher()
digest = hasher.hash(password)

What to ask for in tests

Ask for a test that hashes the same password twice and expects different outputs, and one that verifies a known hash from the old scheme still works during migration. Also check that the password never appears in logs or error messages on failure.

Where Safeguard fits

Safeguard scans dependencies and SBOMs, including the crypto libraries you pick. Try the free scan at gold.safeguard.sh/scan. Parameter choices remain a review call.

Quick check

Is bcrypt acceptable?

OWASP lists it for legacy systems where Argon2id and scrypt are not available, with a work factor of at least 10.

Does a pepper replace a salt?

No. Each password still needs a unique salt. A pepper is an extra shared secret stored away from the database.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.