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.
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.
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.
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. NIST recommends at least 600,000 iterations with HMAC-SHA-256.
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 at least 19 MiB of memory, 2 iterations, and 1 degree of parallelism. 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.
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.
