Best Ways to Store Passwords in a Database: A Secure Hashing Guide
Storing passwords correctly is one of the most critical aspects of web application security. A database breach should never expose user credentials. The golden rule is simple: never store plaintext or reversibly encrypted passwords. Instead, use slow, salted hashing algorithms designed specifically for password verification.
Forget fast algorithms like MD5 or SHA-256; they are far too quick for attackers to brute-force. Modern password storage relies on computationally expensive, adaptive hashing functions. The current industry leaders are Argon2id (the OWASP recommendation), bcrypt, and scrypt, all of which include built-in mechanisms for salting and work factors.
1. Use a Dedicated Password Hashing Algorithm
Choose a password-specific algorithm rather than a generic cryptographic hash:
- Argon2id: The current gold standard; winner of the Password Hashing Competition.
- bcrypt: Widely supported and battle-tested across many frameworks.
- scrypt: Memory-hard design that resists GPU-based attacks.
2. Always Use Per-User Salts
A salt is a unique, random string added to the password before hashing. This prevents rainbow table attacks and ensures identical passwords produce different hashes. Most modern libraries (like bcrypt) generate and embed the salt automatically—never reuse a global salt.
3. Tune the Work Factor
The work factor (cost) controls how slow the hash is to compute. Increase it as hardware improves. As a rule of thumb, hashing should take roughly 100ms. For bcrypt, a cost of 10–12 is common; for Argon2, adjust memory and iterations based on your server’s capabilities.
4. Consider a Pepper for Extra Defense
A pepper is a secret, application-wide value mixed into the password before hashing and stored separately from the database, such as in environment variables or an HSM. It protects against SQL injection cases where only the database is stolen—but never hardcode it in source code.
Conclusion
Secure password storage is non-negotiable. Pair Argon2id or bcrypt with unique salts, keep the work factor high, and store the resulting hash in a dedicated column. Combined with framework defaults and regular audits, your users stay safe even if your database is compromised.