Security
Hashing vs Encryption
If you encrypt passwords, you've already lost. Here's why.
Hashing vs Encryption
If your database stores encrypted passwords, you have a vulnerability, not a security measure. The difference between hashing and encryption is one word, and that word decides whether a stolen database is an inconvenience or a catastrophe.
The difference in one line
Encryption is reversible. Hashing is not.
Encryption is a locked box: put data in with a key, get the original back out with a key. Hashing is a meat grinder: put data in, get mince out, and no amount of cleverness turns mince back into a cow.
Why that matters for passwords
To check a password, you don’t need to know it. You only need to know whether the one just typed matches the one chosen at signup. That’s a comparison, not a retrieval.
So hash the password at signup, store the hash. At login, hash what they typed and compare hashes. The real password never gets stored, which means it can never be stolen from you — not by an attacker, not by a rogue employee, not by you.
Encrypt it instead and you’ve kept the original, just behind a lock. And the key to that lock is on the same server, in an environment variable or a config file, because the app needs it to work. An attacker who gets your database usually gets your environment too. Now they have every password your users own — and because people reuse passwords, you’ve just handed over their email, their bank, their everything.
That’s the whole argument. Encryption keeps the secret. Hashing means you never held it.
Not just any hash
“Hash the password” is necessary but not sufficient. SHA-256 is a hash, and it’s the wrong one here — it’s built to be fast, and fast is precisely what you don’t want. A modern GPU will try billions of SHA-256 guesses per second against your stolen hashes.
Use a hash designed to be deliberately slow and memory-hungry:
- argon2id — the current default recommendation
- bcrypt — older, still respectable, widely available
- scrypt — fine, memory-hard
These take a tunable amount of work per guess. Set it so a legitimate login costs ~100ms, and an attacker’s brute-force costs centuries.
They also salt automatically: a random value mixed into each password before hashing, stored alongside it. Salting means two users with the same password get different hashes, which kills precomputed rainbow tables and stops an attacker cracking one hash and unlocking twenty accounts.
When encryption IS right
Encryption isn’t the villain — it’s just answering a different question. Reach for it when you need the original value back:
- Bank details you must submit to a payment provider
- API keys and OAuth tokens you have to replay to a third party
- Personal data you must display, export, or send in an email
- Message contents in a chat product
The test is simple: will anything ever need to read this value again? If yes, encrypt. If you only ever need to answer “does this match?”, hash.
The rule
Encryption is for data you need to read again. Hashing is for data you only ever need to check. Passwords are the second kind.