Safeguard
Compliance•

PBKDF2 iteration counts for FIPS-bound password storage

If a FIPS-140 requirement rules out Argon2id, PBKDF2 is the option. The OWASP iteration counts for HMAC-SHA-256 and SHA-512 and how to set them.

Safeguard Security Team
3 min read

If a FIPS-140 requirement rules out Argon2id, use PBKDF2. OWASP's guidance lists HMAC-SHA-256 with 600,000 iterations or HMAC-SHA-512 with 220,000. Confirm the numbers against the live cheat sheet before you rely on them, and measure the cost on your own servers.

When PBKDF2 is the answer

OWASP places PBKDF2 last in its ordering, but says to use it when FIPS-140 compliance is required. That is a real constraint in some government, defence and regulated contexts, where only validated modules and approved algorithms are allowed. In those cases, a better algorithm that is not approved is not an option.

If you have no such requirement, prefer Argon2id. PBKDF2 is not memory hard, so attackers with GPUs gain more per dollar against it.

The numbers at the time of fetching

  • HMAC-SHA-256: 600,000 iterations.
  • HMAC-SHA-512: 220,000 iterations.
  • HMAC-SHA-1: 1,400,000 iterations, listed as legacy only.

Do not read the lower SHA-512 count as a sign it is weaker without checking the guidance behind it. These figures change as hardware changes, so a number from a blog post, including this one, is a starting point.

How to set your own value

  1. Pick the hash: HMAC-SHA-256 is the common choice.
  2. Time one derivation on production class hardware.
  3. Estimate login peaks. Multiply the per-hash time by the number of logins per second you must handle.
  4. Raise iterations until you hit your latency or CPU budget, never dropping below the OWASP floor.
  5. Store the iteration count, hash name and salt with each record so you can raise it later.

Check that your module is validated

The algorithm being approved is not enough. For FIPS-140 purposes, the cryptographic module must itself be validated and running in its approved mode. That is a property of the library build and operating system, not of your code. Ask your platform vendor, and keep the validation certificate details with your compliance evidence.

Salts and storage format

Every password needs its own random salt, generated with a cryptographically secure generator and stored beside the hash. The salt is not secret. Its job is to make identical passwords hash differently and to defeat precomputed tables. Store a self-describing string that includes the algorithm, the iteration count and the salt, so a verifier never has to guess which settings a record used.

Also use a constant time comparison when you check a hash, so response timing leaks nothing about how many bytes matched.

Rate limiting protects the cost

A high iteration count turns each login attempt into real CPU work. Add rate limits and lockout logic so an attacker cannot use your login endpoint to burn your capacity. Also cap the password length you accept, so no one can send a huge input to be hashed.

Quick check

Is 600,000 iterations enough forever?

No. It is the OWASP figure at the time we fetched it. Review the cheat sheet periodically and raise the count when guidance changes.

Can I upgrade stored hashes to a higher count?

Yes. At the next successful login, re-derive with the new count and overwrite the record. Store parameters with every hash so this is possible.

Safeguard's compliance and evidence automation can keep your module validation details and policy evidence together, and the free scan at gold.safeguard.sh/scan covers dependencies.

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.