Security
Hashing vs Encryption vs Encoding: Three Different Jobs, One Endless Confusion
· 3 min read
Somewhere right now, a code review contains the phrase 'we encrypt passwords with MD5'. That sentence gets all three concepts wrong at once — MD5 is a hash, not encryption; passwords need neither plain hashing nor encryption but a third thing; and the confusion behind it has real security consequences.
The three transformations look superficially similar (data goes in, gibberish comes out) but make completely different promises. Sorting them out takes ten minutes and pays for a career.
Encoding: reversible by anyone, protects nothing
Encoding re-expresses data in a different alphabet so it survives a channel: Base64 turns bytes into text-safe characters, URL encoding makes strings safe for URLs, HTML entities make text safe for markup. The defining property: reversal requires no secret. Anyone with the output can recover the input by applying the public rules backwards.
Encoding is the right tool when the threat is corruption by the channel, and the wrong tool whenever the threat is a person. 'The token is Base64 so it's protected' is a category error you will meet in real systems; so is its cousin, blocking <script> by filtering while forgetting entity-encoded payloads.
Encryption: reversible with a key, protects confidentiality
Encryption scrambles data so only key holders can read it. Symmetric encryption (AES-GCM) uses one key for both directions and is the workhorse for data at rest and inside TLS sessions. Asymmetric encryption (RSA, elliptic curves) splits the key pair — encrypt with the public key, decrypt with the private — enabling secure channels between strangers and digital signatures.
Encryption's promise is confidentiality, and modern authenticated modes add integrity (tampering is detected). What it does not do: prove who sent something (that's signatures), make data safe to display (that's encoding/sanitization), or protect passwords in a database (keep reading). Practical rule from decades of broken home-rolled crypto: use high-level libraries (libsodium, your platform's vetted primitives), never invent modes, never reuse nonces.
Hashing: irreversible on purpose, protects integrity
A cryptographic hash (SHA-256 and family) maps any input to a fixed-size fingerprint, one way. No key, no reversal — infinite inputs share each output, so the input is mathematically gone. The useful properties: the same input always fingerprints identically; any change avalanche-changes the output; and finding two inputs with the same fingerprint (a collision) is computationally infeasible for current algorithms.
That makes hashes the tool for integrity and identity-of-content: file checksums, git commits, cache keys, content-addressed storage, signature inputs. Algorithm status in 2026: SHA-256/SHA-512 are the defaults; SHA-1 collisions are practical (the SHAttered PDFs) so it is legacy-only; MD5 collisions are trivial — corruption detection at best, never security.
Passwords: the fourth category everyone forgets
Passwords break the plain-hashing model because humans choose from a tiny, predictable space. A GPU computes billions of SHA-256 guesses per second; every leaked-password list and mangling rule is in the attacker's dictionary. Fast hashes — however 'secure' cryptographically — let attackers test the whole human password space in hours.
Password storage therefore uses deliberately expensive, salted functions: Argon2id (current OWASP first choice), scrypt, or bcrypt. The salt (random, per-password, stored alongside) kills precomputed rainbow tables; the tunable cost turns billions of guesses per second into thousands. This is why 'we encrypt passwords' is wrong twice: encryption is reversible (a stolen key un-scrambles every password), and plain hashing is too fast. The correct sentence is 'we hash passwords with Argon2id'.
The decision table
- Data must survive a text channel (binary in JSON, values in URLs) → encode (Base64, URL encoding). Reversible by design.
- Data must be secret from non-key-holders (PII at rest, transport) → encrypt (AES-GCM via a vetted library; TLS in transit).
- Data must be verifiable as unchanged, or identified by content (checksums, cache keys, dedup) → hash (SHA-256).
- A request must be provably from a secret-holder (API signing, webhooks) → HMAC — hash + key, the hybrid that plain 'hash(secret+message)' gets wrong via length-extension.
- A human-chosen password must be checkable without being recoverable → password hash (Argon2id/bcrypt) with per-password salt.
Self-test with real tools
The distinctions stick fastest when you handle them: Base64-encode a sentence and decode it back with no key — that's encoding's whole security story. Hash the same sentence twice and watch the fingerprint repeat; change one letter and watch it avalanche. Generate an HMAC and see how the same message with a different key yields a different tag. Ten minutes of playing beats any amount of rereading.