Security•Jun 2026•4 min read

Hashed Passwords vs Plain Text Passwords

Storing passwords as one-way hashes versus storing them as readable text. One is the baseline for not getting sued; the other is professional negligence.

The short answer

Hashed Passwords over Plain Text Passwords for most cases. Plain text means your database is a credential leak waiting to happen, and a single read — a backup, a log line, a SQL injection, a disgruntled DBA — hands.

  • Pick Hashed Passwords if storing passwords at all — i.e. always. Use bcrypt, scrypt, or Argon2id with a per-user salt and an appropriate work factor
  • Pick Plain Text Passwords if never. There is no legitimate engineering reason to store a recoverable password in plain text. If you think you have one, you have a design bug
  • Also consider: If you don't want to store passwords at all, skip the debate entirely: use OAuth/OIDC, magic links, or passkeys (WebAuthn) and let someone else hold the secret.

— Nice Pick, opinionated tool recommendations

The verdict, stated plainly

Hash your passwords. This isn't a tradeoff with two reasonable sides — it's correct versus negligent. A plain-text password store means that the moment anyone reads your database, every user's credential is exposed in usable form. And databases get read constantly: nightly backups land on S3, queries get logged, ORMs dump rows into error trackers, contractors get read replicas, and SQL injection turns a single bad query into a full credential dump. A proper hash — bcrypt, scrypt, or Argon2id, salted per user — means that same breach yields gibberish that costs an attacker enormous compute per guess. The asymmetry is the whole point: cheap for you to verify a login, brutally expensive for an attacker to reverse. People who store plain text aren't making a different bet; they just haven't been breached yet. The clock is the only variable.

Why plain text is indefensible

The classic excuse is 'we need to email users their password if they forget.' That feature is the bug. If your system can recall a password, so can anyone who compromises your system. The correct password-reset flow never recovers the old secret — it issues a time-limited token and lets the user set a new one, which works perfectly with hashes. Other excuses are worse: 'it's an internal tool,' 'it's a prototype,' 'we'll fix it later.' Internal tools get breached. Prototypes ship. 'Later' becomes the incident post-mortem. Password reuse compounds the damage: your leaked plain-text store doesn't just burn accounts on your app, it hands attackers credentials they'll replay against banks, email, and everything else your users were careless enough to reuse. You don't get to opt out of that liability. Storing plain text is a choice to make your worst day catastrophic instead of merely bad.

Hashing done right (and wrong)

Hashing isn't a magic word — you can do it badly. MD5 and SHA-256 are NOT password hashes; they're fast, which is exactly wrong, because fast means a GPU farm tests billions of guesses per second. Use a deliberately slow, memory-hard algorithm: Argon2id is the current pick, bcrypt is the boring safe default, scrypt is fine. Always salt per user — a unique random salt per row defeats rainbow tables and stops identical passwords from producing identical hashes. Tune the work factor so a single hash takes ~100–250ms on your hardware, and revisit it as hardware improves. A common upgrade path: wrap existing weak hashes (peppered, or via a slow algorithm) and rehash on next login. And never invent your own scheme — 'salt + SHA1 + clever rounds' is how juniors reconstruct 2009-era mistakes. Use the library your language already ships; it has thought about timing attacks so you don't have to.

The honest caveat: don't store them at all

The cleanest win is sidestepping the whole question. If you can avoid being a password custodian, do it. OAuth/OIDC ('Sign in with Google/GitHub') pushes the credential-storage liability onto providers who staff teams for exactly this. Magic links trade a stored secret for a one-time emailed token. Passkeys (WebAuthn) replace passwords with device-bound public-key cryptography — there's literally nothing reversible to steal, and they're phishing-resistant by design. These aren't always available: you may need offline auth, machine accounts, or you're building the identity provider yourself. In those cases, you store hashes, full stop. But if your reflex is 'add a users table with a password column,' pause and ask whether you need to hold that secret at all. The best-protected password is the one you never stored. When you must store one, hash it properly — and if you're reading this to decide between the two, you already know which side Nice Pick is on.

Quick Comparison

FactorHashed PasswordsPlain Text Passwords
Impact of a database breachAttacker gets salted slow hashes — brute-forcing each is expensive; strong passwords stay safe long enough to rotateAttacker instantly gets every usable credential, ready to replay against other sites
Password reset / recoveryIssue a one-time reset token, user sets a new password — never recovers the old oneCan email the old password back, which is precisely the security hole
Compliance & liability (GDPR, SOC 2, PCI)Meets the baseline expectation for credential storageFails audits outright; negligence in any breach lawsuit
Implementation effortOne library call (bcrypt/argon2) to hash, one to verify — minutesMarginally less code; you skip a function call to save a column
Performance at loginIntentionally slow (~100-250ms per verify) — the cost is the featureA trivial string compare, faster but irrelevant

The Verdict

Use Hashed Passwords if: You are storing passwords at all — i.e. always. Use bcrypt, scrypt, or Argon2id with a per-user salt and an appropriate work factor.

Use Plain Text Passwords if: Never. There is no legitimate engineering reason to store a recoverable password in plain text. If you think you have one, you have a design bug.

Consider: If you don't want to store passwords at all, skip the debate entirely: use OAuth/OIDC, magic links, or passkeys (WebAuthn) and let someone else hold the secret.

🧊
The Bottom Line
Hashed Passwords wins

Plain text means your database is a credential leak waiting to happen, and a single read — a backup, a log line, a SQL injection, a disgruntled DBA — hands every account to the attacker. Hashing with a slow, salted algorithm makes a breach survivable instead of catastrophic. There is no scenario where plain text wins; this is the most one-sided comparison Nice Pick will ever publish.

Related Comparisons

Disagree? nice@nicepick.dev