Password Security: Best Practices and Common Mistakes
Password security spans two very different responsibilities: how users choose and manage their passwords, and how systems store and verify them. Both sides fail in predictable ways, and attackers exploit the gaps with cracking tools, leaked databases, and guessing at scale. This guide explains the practices that actually protect credentials, from modern password hashing to sensible policy, and highlights the common mistakes that quietly weaken otherwise reasonable systems.
How Passwords Should Be Stored
The single most important rule is that systems must never store passwords in plaintext or with reversible encryption. If the database is stolen, plaintext passwords hand every account to the attacker instantly. Instead, servers store a one-way cryptographic representation and verify by recomputing it.
Hashing, Not Encryption
A password hash is a one-way function: given the password you can compute the hash, but from the hash you cannot feasibly recover the password. On login the server hashes the submitted password and compares it to the stored value. Encryption is the wrong tool because it is reversible, meaning a stolen key exposes every password.
Use a Slow, Purpose-Built Algorithm
General-purpose hashes like MD5, SHA-1, and even SHA-256 are designed to be fast, which is exactly what an attacker wants when guessing billions of candidates. Password storage should use a deliberately slow, memory-hard algorithm built for the job:
- Argon2 is a modern memory-hard function and a strong default.
- scrypt is also memory-hard and widely supported.
- bcrypt remains a solid, battle-tested choice.
- PBKDF2 is acceptable where the others are unavailable, with a high iteration count.
These functions expose a cost parameter, letting defenders tune how expensive each hash is. The cost should be set as high as the server can tolerate, so that an attacker who steals the database faces a punishing per-guess cost.
Salts and Peppers
Two additional ingredients defeat precomputation and mass cracking.
- A salt is a unique random value stored alongside each hash and mixed into the computation. Because every user's salt differs, identical passwords produce different hashes, which defeats rainbow tables and prevents an attacker from cracking many accounts with one effort. Modern algorithms like Argon2 and bcrypt handle salting for you.
- A pepper is a secret value applied to all passwords but stored separately from the database, for example in application configuration or a hardware security module. If only the database leaks, the missing pepper leaves the attacker unable to verify guesses.
A common mistake is reusing a single static salt for every user, which reintroduces exactly the weakness salts exist to remove.
Sensible Password Policy
Policy should make strong passwords easy and weak ones impossible, without pushing users toward predictable workarounds.
- Favor length over complexity. A long passphrase is stronger and more memorable than a short string of mixed symbols. Enforce a generous minimum length rather than convoluted composition rules.
- Screen against known-bad passwords. Reject passwords found in breach corpora and common-password lists. This blocks the guesses attackers try first and is more effective than arbitrary character requirements.
- Do not force periodic rotation without cause. Mandatory frequent expiry drives users to trivial variations like appending a number. Rotate only on evidence of compromise.
- Allow the full character set and long inputs. Silently truncating or banning special characters weakens passwords and frustrates password managers.
- Support password managers. Do not block paste, and do not interfere with autofill, since managers enable long unique passwords per site.
Common Mistakes That Weaken Passwords
Even teams that hash correctly often undermine themselves elsewhere.
- Fast or unsalted hashes. Storing SHA-256 without a slow function leaves hashes crackable at enormous speed.
- Verbose login errors. Telling an attacker whether the username or the password was wrong aids account enumeration; return a single generic failure.
- No rate limiting. Without throttling and lockout, online guessing and credential stuffing run unchecked.
- Leaking hashes in logs or backups. Password material can escape through debug logs, error traces, and unsecured backups.
- Weak reset flows. A password reset that relies on guessable security questions or unexpiring tokens becomes the easiest way in.
A short illustration of correct verification pseudocode:
stored = db.lookup(user) # contains algorithm, cost, salt, hash
if stored is None:
argon2.verify(dummy_hash, input) # constant-time to avoid timing leaks
return generic_failure
if argon2.verify(stored, input):
if argon2.needs_rehash(stored): # cost increased since storage
db.update(user, argon2.hash(input))
return success
return generic_failure
The dummy verification for unknown users keeps response timing uniform, so an attacker cannot distinguish valid usernames by how quickly the server replies.
Passwords Are Not Enough Alone
Even flawless password handling cannot stop phishing or reuse of a password leaked from another site. Passwords should be one layer, reinforced by multi-factor authentication and, where possible, replaced entirely by FIDO2 and WebAuthn passkeys. Encouraging users to adopt a password manager, unique passwords per site, and a strong second factor does more for real-world safety than any single storage improvement. More defensive guidance is available across K0G.
Key Takeaways
- Never store passwords reversibly; hash them with a slow, memory-hard algorithm such as Argon2, scrypt, or bcrypt.
- Salt every password uniquely to defeat rainbow tables, and consider a separately stored pepper for defense in depth.
- Prioritize length and breach screening over arbitrary complexity rules, and avoid needless forced rotation.
- Rate-limit logins, return generic error messages, and secure reset flows to close common bypasses.
- Treat passwords as one layer and reinforce them with multi-factor authentication or passwordless methods.