- Why Secure Password Storage Matters
- What Security Assumptions Does This Analysis Rest On?
- Understanding Hashing and Password Salting
- A Worked Example: Why Algorithm Choice Changes Everything
- bcrypt Explained: Strengths, Weaknesses, and When to Use It
- Argon2 vs. PBKDF2 vs. bcrypt: Key Differences That Matter
- What Are the Limitations of Hashing-Based Password Storage?
- Which Algorithm Should You Use? An Enterprise Decision Table
- Best Practices for Storing Passwords Securely
- How Encryption Consulting Can Help
- Frequently Asked Questions
- Conclusion
Quick answer: Secure password storage means hashing every password with a unique random salt using a slow, memory-hard algorithm, never storing it in plaintext or reversible encryption. For new systems, use Argon2id with OWASP’s current parameters; for legacy systems, keep bcrypt with a cost factor of at least 12; use PBKDF2 only where FIPS validation requires it. The recommended action: audit your current hashing algorithm and parameters today, since this is a cryptographic implementation decision that is easy to get wrong silently.
Published: June 2026 | Updated: August 2026 | Reviewed by the Encryption Consulting cryptography advisory team
Most people never think about how their passwords are stored after clicking Sign Up. They just trust that companies are handling it the right way. But that trust is not always earned. Password storage is one of the most commonly mishandled areas in application security, and the consequences of getting it wrong are serious.
Every year, breaches expose millions of user credentials. In many cases, those credentials were stored in ways that made cracking them easy. No salting, weak hashes, sometimes even plaintext. These are not edge cases. They are recurring failures that affect real users.
This post explains what secure password storage actually looks like, from the basics of hashing and salting to a practical comparison of bcrypt, Argon2, and PBKDF2. If you are building or reviewing an authentication system, this is where to start.
Why Secure Password Storage Matters
When attackers steal a password database, they do not instantly have everyone’s passwords. What they have is a set of stored representations. If those were created properly, the data is largely useless to them. If not, they can recover thousands of real passwords in a matter of hours.
The risk does not stop at one account. People reuse passwords across services. A cracked password from a low-stakes app can unlock email accounts, banking logins, or corporate systems. That chain reaction is why even small applications carry responsibility for their users’ broader security.
There is also a compliance dimension. Frameworks like NIST SP 800-63B, GDPR, and PCI-DSS all carry expectations around how authentication data is protected. Poor password storage is both a technical failure and a regulatory one, with penalties and reputational damage to follow.
What Security Assumptions Does This Analysis Rest On?
Every recommendation in this post assumes a specific threat model: an attacker who has obtained the password database offline, through a breach, an insider, or a misconfigured backup, and is free to run unlimited guessing attempts without rate limiting, account lockouts, or monitoring. This is the correct assumption to design against, because online rate limiting is a control that can fail or be bypassed, while offline cracking resistance is a property of the stored data itself.
The analysis also assumes the attacker has access to commodity GPU hardware, not a nation-state ASIC farm; assumes passwords come from real user behavior, meaning a meaningful fraction are short, dictionary-based, or reused, not uniformly random; and assumes the salt is public (stored alongside the hash, as it should be) so the algorithm’s cost, not the salt’s secrecy, is what has to resist the attack.
Understanding Hashing and Password Salting
A cryptographic hash function takes a password as input and produces a fixed-length output called a hash or digest. The key property is that this process only goes one way. You cannot reverse a hash to get the original password back. So instead of storing passwords directly, systems store their hashes. At login, the entered password is hashed and compared to what was stored.
This sounds secure enough. But general purpose hash functions like MD5 and SHA-256 were built for speed, not for passwords. A modern GPU can compute billions of SHA-256 hashes per second. That speed is an advantage for attackers running brute-force or dictionary attacks.
Password salting addresses a specific attack: rainbow tables. A rainbow table is a precomputed lookup of hashes for common passwords. An attacker can take a stolen hash and look it up in seconds without doing any real computation.
A salt is a unique, randomly generated string added to each password before hashing. Even if two users have the same password, their salts make the resulting hashes completely different. This makes rainbow tables useless and forces attackers to crack each hash individually.
Salts are not secret. They are stored alongside the hash. Their value comes from being unique and random, not from being hidden. Salting is not encryption. It is a way to defeat precomputation attacks.
A Worked Example: Why Algorithm Choice Changes Everything
Numbers make the difference concrete. Take an 8-character password drawn from lowercase letters only, roughly 208 billion possible combinations. A single modern consumer GPU can compute upward of 10 billion unsalted SHA-256 hashes per second, which means that keyspace is exhaustible in well under a minute, salt or no salt, because salting stops precomputed rainbow tables but does not slow down a per-hash brute-force attempt against fast hashes.
Run the same attack against a password stored with Argon2id at OWASP’s recommended baseline (46 MiB of memory, 1 iteration, 1 degree of parallelism), and the picture changes. The same GPU that computed 10 billion SHA-256 hashes per second is now bottlenecked by memory bandwidth, not raw compute, and can typically manage only a few thousand Argon2id hashes per second at that memory cost. The same 208 billion combinations that took under a minute against raw SHA-256 now takes months to years of sustained GPU time, and scaling the attack requires provisioning enough memory-equivalent hardware to run many instances in parallel, which is far more expensive than adding GPU cores. That gap, not a difference in mathematical strength, is the entire reason memory-hard algorithms exist.
bcrypt Explained: Strengths, Weaknesses, and When to Use It
bcrypt was created in 1999 specifically for password hashing. It includes automatic salting and a configurable cost factor, also called a work factor, that controls how computationally expensive the hashing is. Increase the cost factor and you increase the time needed per hash, which slows down attackers trying to crack passwords at scale.
Strengths:
- Built-in salting: bcrypt generates and stores a unique salt per password automatically, removing a common source of developer error.
- Adaptive cost factor: As hardware improves, you can raise the work factor to maintain resistance against attacks.
- Battle-tested: Over 25 years of scrutiny with no fundamental vulnerabilities found.
- Wide support: Available in virtually every major programming language and framework.
Limitations:
- 72-character limit: bcrypt truncates inputs beyond 72 bytes. This can be an issue for long passphrases if not handled properly.
- No memory-hardness: bcrypt does not require significant RAM, which makes it more vulnerable to highly parallel hardware attacks compared to newer options.
Argon2 vs. PBKDF2 vs. bcrypt: Key Differences That Matter
Not all password hashing algorithms offer the same level of protection. Here is how the three main options compare across the factors that matter most.
| Feature | bcrypt | Argon2 | PBKDF2 |
|---|---|---|---|
| Memory-hardness | Not memory-hard | Memory-hard with configurable RAM usage | Not memory-hard |
| Built-in salting | Yes, automatic | Yes, automatic | No, must be handled manually |
| Parallelism control | Not supported | Supported via Argon2id | Not supported |
| Password length limit | Capped at 72 bytes | No limit | No limit |
| NIST recommended | Not listed in SP 800-63B | Yes | Yes |
| GPU resistance | Moderate | High | Low to moderate |
| Maturity | Over 25 years | Available since 2015 | Available since 2000 |
Argon2: The Modern Standard
Argon2 won the Password Hashing Competition in 2015 and is recommended by NIST in SP 800-63B. It comes in three variants. Argon2d resists GPU attacks. Argon2i resists side-channel attacks. Argon2id combines both and is the recommended choice for most password storage scenarios.
What sets Argon2 apart is memory-hardness. It requires a configurable amount of RAM during computation. This makes parallel attacks on specialized hardware like ASICs or FPGAs significantly more expensive. More memory cost means fewer simultaneous cracking attempts an attacker can run. OWASP’s current guidance lists 46 MiB of memory with 1 iteration and 1 degree of parallelism as the preferred baseline, with a leaner 19 MiB, 2-iteration configuration as an acceptable alternative for memory-constrained environments.
PBKDF2: The Compliance Option
PBKDF2 is the oldest of the three and is widely used in FIPS-validated environments because it is built on approved HMAC constructions. If your organization requires FIPS 140-2 or 140-3 compliance, common in federal and regulated financial sectors, PBKDF2 with SHA-256 or SHA-512 may be required. It lacks memory-hardness, which makes it the weakest of the three against hardware attacks. Compensate with a high iteration count: current guidance calls for at least 600,000 iterations with HMAC-SHA-256, or roughly 210,000 iterations with the more computationally expensive HMAC-SHA-512.
What Are the Limitations of Hashing-Based Password Storage?
- Hashing protects the stored database, but it does nothing against credential stuffing, phishing, or password reuse; a correctly hashed password stolen through a phishing page is compromised regardless of algorithm.
- Algorithm strength cannot compensate for weak user passwords; a common word protected by Argon2id is still crackable in a targeted dictionary attack, just more slowly than under a fast hash.
- Increasing memory cost or iteration count raises server-side resource usage at login time, so parameter tuning is a real capacity-planning tradeoff, not a free security upgrade.
- Migrating an existing user base to a new algorithm cannot happen instantly, since old hashes cannot be converted without the plaintext password; migration has to happen progressively, at each user’s next successful login.
Which Algorithm Should You Use? An Enterprise Decision Table
| Scenario | Recommended algorithm | Suggested parameters |
|---|---|---|
| New application, no legacy constraint | Argon2id | 46 MiB memory, 1 iteration, 1 degree of parallelism (OWASP baseline) |
| Existing system already using bcrypt | Keep bcrypt, plan migration | Cost factor of at least 12; re-hash to Argon2id progressively at next login |
| FIPS 140-2/140-3 validated environment | PBKDF2-HMAC-SHA256 | At least 600,000 iterations, unique random salt per password |
| High-value target / elevated threat model | Argon2id, higher memory cost | 64 MiB or more, tuned to acceptable login latency on your hardware |
| Resource-constrained (mobile, embedded, serverless) | Argon2id, leaner profile | 19 MiB memory, 2 iterations, 1 degree of parallelism |
Best Practices for Storing Passwords Securely
Choosing the right algorithm is only the starting point. Here is what a solid implementation should include:
- Choose the right algorithm: Use Argon2id for new systems. Keep bcrypt for existing systems with a cost factor of 12 or higher. Use PBKDF2 only when compliance requires it.
- Tune your parameters: For Argon2id, OWASP recommends 46 MiB of memory, 1 iteration, and 1 degree of parallelism as the preferred baseline. Adjust upward based on your server capacity and acceptable login latency.
- Always use unique, random salts: Even if your library handles salting automatically, understand that it does. Never use static values or sequential identifiers as salts.
- Enforce strong passwords: Hashing does not replace good password policy. Require minimum lengths, reject known breached passwords, and support multi-factor authentication.
- Separate credential storage: Store password hashes in a dedicated store with strict access controls. Applications should only access credential data at authentication time.
- Plan for algorithm migration: Design your system to re-hash credentials at next login. This lets you upgrade algorithms or parameters without forcing a mass password reset.
How Encryption Consulting Can Help
Getting password storage right is a cryptographic implementation decision, and like most cryptographic decisions, it is easy to get wrong in ways that are not immediately visible. The wrong algorithm, a missing salt, an insufficient iteration count, or a FIPS-incompatible implementation can all look fine on the surface while quietly exposing your organization to serious risk. That is where Encryption Consulting’s Encryption Advisory Services come in.
Our team helps organizations assess and strengthen their cryptographic implementations across cloud, on-premises, and hybrid environments. Whether you are building a new authentication system, reviewing an existing one, or preparing for a compliance audit under PCI-DSS, NIST SP 800-63B, or GDPR, we bring the depth to evaluate what is actually in place and the practical experience to help you fix what is not right.
Here is where our Encryption Advisory Services apply directly to password storage and authentication security:
Cryptographic Implementation Review: We assess your current password storage implementation, including algorithm choice, salting practices, parameter tuning, and how credential data is separated and access controlled. This gives you a clear picture of where the gaps are before a breach or audit surfaces them.
Algorithm Selection and Migration Planning: Moving from bcrypt to Argon2id, or from a legacy PBKDF2 implementation to one that meets current NIST iteration count recommendations, requires careful planning to avoid breaking existing authentication flows. We design migration paths that re-hash credentials progressively at next login, with no forced password resets and no user disruption.
FIPS Compliance Alignment: For organizations operating in federal or regulated financial environments where FIPS 140-2 or 140-3 validation is required, we help you select and configure PBKDF2 implementations that meet compliance requirements while compensating for the algorithm’s weaker hardware resistance through proper parameter configuration.
Compliance Gap Analysis: Frameworks like PCI-DSS, GDPR, and NIST SP 800-63B all carry expectations around how authentication data is protected. We map your current implementation against these requirements, identify gaps, and produce a clear remediation roadmap.
Poor password storage is one of the most common and most avoidable security failures. If your organization has not formally reviewed its credential handling practices, now is the right time.
Frequently Asked Questions
Which password hashing algorithm should a new project use?
Argon2id, using OWASP’s current baseline of 46 MiB of memory, 1 iteration, and 1 degree of parallelism. It is memory-hard, NIST-recommended, and has no practical password length limit.
Is bcrypt still safe to use?
Yes, with a cost factor of at least 12. bcrypt has over 25 years of scrutiny with no fundamental breaks, but it lacks memory-hardness, so new systems should prefer Argon2id where there is no legacy constraint.
Why does salting alone not stop fast brute-force attacks?
Salting defeats precomputed rainbow tables by making every hash unique, but it does not slow down the per-hash computation itself. A fast, unsalted-equivalent hash function like raw SHA-256 can still be brute-forced quickly per password even with a unique salt; only a deliberately slow, memory-hard algorithm resists that.
When is PBKDF2 the right choice instead of Argon2id?
When FIPS 140-2 or 140-3 validation is a hard requirement, since PBKDF2 is built on approved HMAC constructions that Argon2 currently is not. Compensate for its lack of memory-hardness with at least 600,000 iterations of HMAC-SHA-256.
How should an organization migrate from an older algorithm to Argon2id?
Progressively, at each user’s next successful login: verify against the old hash, then re-hash the password with Argon2id and store the new hash. This avoids a forced mass password reset while completing the migration over time.
Conclusion
Password storage is not a glamorous topic, but it is foundational. The choices made at the data layer determine whether a breach becomes a contained incident or a much larger problem. Every other security control in an application ultimately depends on how credentials are stored, and given how often people reuse passwords, the damage from a weak implementation often extends well beyond the original application.
What makes this area tricky is that the consequences are rarely visible until something goes wrong. A weak hashing choice does not slow performance or trigger alerts. It sits quietly in production until a breach occurs and attackers start cracking the stored hashes. The good news is that the guidance here is not ambiguous. NIST has published clear recommendations, and mature libraries exist in every major language to implement these algorithms correctly.
The path forward is clear. Use Argon2id for new implementations. Maintain bcrypt responsibly for legacy systems with a cost factor of at least 12. Reach for PBKDF2 only when compliance requires it, compensating with a high iteration count. Always salt, always tune your parameters, and plan for future algorithm migrations from the start. Treat this as an ongoing practice, not a one-time setup.
References
- Why Secure Password Storage Matters
- What Security Assumptions Does This Analysis Rest On?
- Understanding Hashing and Password Salting
- A Worked Example: Why Algorithm Choice Changes Everything
- bcrypt Explained: Strengths, Weaknesses, and When to Use It
- Argon2 vs. PBKDF2 vs. bcrypt: Key Differences That Matter
- What Are the Limitations of Hashing-Based Password Storage?
- Which Algorithm Should You Use? An Enterprise Decision Table
- Best Practices for Storing Passwords Securely
- How Encryption Consulting Can Help
- Frequently Asked Questions
- Conclusion
