Library
9 min read

Hashing vs encryption vs encoding

Published

Three words that get used as if they were the same. They are not even close, and the quickest way to tell them apart is one question: who can get the original back?

  • Encoding: anyone. It just changes the format so data survives a trip, like writing a phone number in words so it fits a form. Base64, URL encoding, UTF-8.
  • Hashing: nobody. It turns any input into a fixed size fingerprint, and there is no way back. SHA-256, bcrypt, Argon2.
  • Encryption: only whoever holds the key. AES, ChaCha20, RSA.
Your answer

A startup stores user passwords encrypted with AES, with the key in an environment variable, so they can 'recover' a password for users who forget it. Is that safe? What would you change?

Try it#

Type anything below. The page computes all three for real in your browser.

One input, three transformations
Encoding (Base64)Anyone can reverse it
dXBpLXBpbjo0ODIx
Decoded with no key: "upi-pin:4821"
Hashing (SHA-256)Nobody can reverse it
...
Encryption (AES-256-GCM)Only the key holder can reverse it
...
Encoding hid nothing: anyone can turn it back. The hash is always 32 bytes whatever you type, and changing one character flipped 0 of its 256 bits, so it cannot be nudged backwards. Encryption ran twice on the same text and gave two different outputs (a random IV each time), yet the key holder recovers the exact original.
Everything runs in your browser with the Web Crypto API. The AES key was generated when the page loaded and never leaves this tab.

Watch three things as you type. The Base64 row grows with your input and decodes back without any key, so it hides nothing. The SHA-256 row is always 64 hex characters, and a one character change scrambles about half its bits, which is called the avalanche effect. The AES row is different every time you encrypt the same text, because each encryption uses a fresh random IV, yet the key holder gets the exact original back.

Encoding: for compatibility, never for secrecy#

Base64 exists because some channels only carry text: email bodies, JSON fields, URLs, HTTP headers. It maps every 3 bytes to 4 printable characters, so a file grows by a third. It is completely public. If you see a "secret" that ends in ==, it is probably Base64, and it is not secret. Kubernetes Secrets are Base64 encoded by default, which surprises people every year: anyone who can read the object can read the secret. Encryption at rest has to be turned on separately.

Hashing: fingerprints#

A cryptographic hash is a one way function with three promises: the same input always gives the same output; you cannot find an input from its output; and you cannot find two inputs with the same output. Uses:

  • Integrity. Download a Linux ISO, compare its SHA-256 with the published one.
  • Deduplication and addressing. Git names every object by its hash. Dropbox style sync identifies chunks by hash.
  • Passwords. Store a hash, not the password. At login, hash what the user typed and compare.

Why SHA-256 is wrong for passwords#

SHA-256 is built to be fast. That is great for checking a 4 GB file. It is terrible for passwords, because an attacker who steals your hash table can try guesses as fast as the hash runs. Password hashes are built to be slow and memory hungry on purpose, with a salt (a random value per user, stored next to the hash) so identical passwords get different hashes and precomputed tables are useless.

How long to try every password, on one gaming GPU
8 characters
upper, lower, digits
SHA-256
Possible passwords62^8 = 2.18e+14
Guesses per second2.2e+10
3 hours
to try every one (half that on average)

Fast hashes let one consumer graphics card try billions of guesses a second. Short passwords fall in minutes. Now switch the storage to bcrypt and keep everything else the same.

The rates are rounded from public hashcat benchmarks on a single RTX 4090. Real attackers use many cards and start with lists of leaked passwords rather than brute force, so real passwords fall much faster than this. The ratio between rows is the point.

What to use in 2026: Argon2id (winner of the Password Hashing Competition in 2015, and OWASP's first choice), or bcrypt with a cost of at least 10 if that is what your platform has, or scrypt. All three include the salt in their output string, so you store one column:

text
$argon2id$v=19$m=19456,t=2,p=1$c29tZXNhbHQ$wJq3...  <- algorithm, settings, salt, hash
$2b$12$R9h/cIPz0gi.URNNX3kh2OPST9/PgBkqquzi.Ss7KIUgO2t0jWMUW  <- bcrypt, cost 12

Encryption: for data you need back#

Encrypt when you must read the data again: card numbers a payment processor charges later, Aadhaar numbers for KYC, messages, backups, disks. Symmetric encryption (AES) uses one key to lock and unlock and is very fast. Asymmetric encryption (RSA, elliptic curves) uses a public key to lock and a private key to unlock and is much slower. Real systems use both; the details are in symmetric vs asymmetric encryption.

The hard part is never the algorithm. It is the key: where it lives, who can use it, how it rotates. A database encrypted with a key stored in the same database is decoration. Use a key management service (AWS KMS, Google Cloud KMS, HashiCorp Vault) so the application asks for decryption and never holds the master key.

Two famous breaches, two different mistakes

LinkedIn, 2012. About 6.5 million password hashes were posted online; years later the full set turned out to be around 117 million. They were unsalted SHA-1, so most were cracked quickly, and identical passwords had identical hashes.

Adobe, 2013. Around 150 million records. The passwords were encrypted (3DES in ECB mode) instead of hashed, with one key for everyone. Same password, same ciphertext, and the plain text password hints sat right next to them. People solved it like a giant crossword.

ChoiceWhat you gainWhat you payPick it when
Encoding (Base64, URL, hex)Makes binary data safe for text channels.Zero secrecy. Anyone can decode it.Sending images in JSON, tokens in headers, data in URLs.
Fast hash (SHA-256, BLAKE3)Fixed size fingerprint, one way, very fast.Too fast for passwords. Not reversible, so useless when you need the data back.Integrity checks, content addressing, HMAC signatures.
Password hash (Argon2id, bcrypt, scrypt)Slow, salted, memory hard. Makes stolen hashes expensive to crack.Deliberately costs 50 to 250 ms of CPU per login.Passwords, PINs, API keys you only need to verify.
Encryption (AES-GCM, ChaCha20-Poly1305)Data comes back exactly, for whoever holds the key.Key management is the whole problem.Data you must read later: PII, card data, backups, traffic.

In the interview#

Interviewer
How would you store passwords?
A screening question. A crisp, complete answer takes thirty seconds.
You
Never the password itself, and never encrypted, because then anyone with the key gets every password. I would hash with Argon2id, or bcrypt at cost 12 if Argon2 is not available, tuned so one hash takes around 100 milliseconds on our hardware. The library adds a unique salt per user and stores it in the output. At login I verify with the library's constant time compare. I would also rate limit login attempts and check new passwords against a list of known breached ones.
Says what not to do and why, names a tuning target, and adds two defences beyond storage.
Interviewer
A user forgot their password. Can you email it to them?
You
No, and if a system can, it is storing passwords reversibly, which is a red flag. I would email a single use reset link with a random token that expires in fifteen minutes or so, store only a hash of that token, and invalidate the user's sessions after the reset.
Hashing, encryption or encoding?
The Authorization: Basic header
Git commit ids
WhatsApp messages in transit
Comparing a download against its published checksum
%20 in a URL

Checkpoint#

Checkpoint

1. Why is encrypting passwords worse than hashing them?

2. Why not hash passwords with SHA-256 plus a salt?

3. What does a salt protect against?

Worth memorising
  • Who gets it back? Encoding: anyone. Hashing: nobody. Encryption: the key holder.
  • Base64 is not security. Neither are Kubernetes Secrets by default.
  • Passwords: Argon2id or bcrypt, salted, about 100 ms per hash. Never SHA-256, never encryption.
  • Encryption's hard part is the key. Use a KMS.
Say this in 60 seconds

Encoding, hashing and encryption differ in who can get the original back. Encoding, like Base64, only changes the format so data survives a text channel, and anyone can reverse it, so it provides no secrecy. Hashing produces a fixed size fingerprint that cannot be reversed; fast hashes like SHA-256 are for integrity and content addressing, while passwords need deliberately slow, salted hashes like Argon2id or bcrypt so stolen hashes cost an attacker years instead of minutes. Encryption is reversible with a key, used for data you must read again like PII, messages and backups; AES is the symmetric workhorse, and the hard part is key management, which belongs in a KMS. I never encrypt passwords, because one leaked key exposes them all, and the app only ever needs to verify them.