viewer.csslab.dev

Hashing, encryption, and encoding are three different things

Encoding changes the shape of data so it can travel somewhere; it uses no key and anyone can undo it. Encryption changes data so only a key holder can read it; it is meant to be undone, by them. Hashing produces a fixed-size fingerprint that cannot be undone by anyone, including whoever made it. The three get confused because all of them turn readable text into unreadable text — but only one of them protects anything, and only one of them is meant to be reversed.

Encoding: a change of shape

Base64, URL percent-encoding, and hex are encodings. Their job is to let arbitrary bytes travel through a channel that only accepts a restricted set of characters — an email body, a URL, an HTTP header. There is no key involved, the transformation is public, and reversing it is a function call.

This is why calling Base64 "encryption" is not a small imprecision but the opposite of the truth. Anyone who sees the string can read it. A password stored as Base64 is a plaintext password with extra steps, and it will be read by the first person who looks at the database.

Encryption: a change only a key holder can undo

Encryption transforms data so that only someone with the right key can recover it. That is the entire point: the data is meant to come back, for the right person, later. AES protecting a file at rest and TLS protecting a request in transit are both this.

Because it is reversible by design, encryption is what you want whenever the data has to be readable again — a stored access token you will send later, a document a user will open tomorrow, a field you must decrypt to display.

Hashing: a fingerprint, one way

A hash function turns any input into a fixed-size value. The input is not stored inside the output, so there is nothing to reverse — not with a key, not with more computation, not by the person who wrote the function. SHA-256 gives 32 bytes whether the input was one character or a gigabyte.

That is what makes it useful for the two things it is actually for: checking that data arrived unchanged, and comparing two things without storing either. A password check works this way — the system stores the hash, hashes what you typed, and compares.

Sites offering to "decrypt" a hash are looking the value up in a table of previously computed inputs. That works for common passwords and never for a random one, which is the whole argument for salting: a salt makes the precomputed table useless because every stored hash is now for a different input.

Where the three get mixed up

A JWT is the clearest example. Its header and payload are Base64url-encoded, not encrypted, so anyone holding the token can read every claim in it. The signature is a keyed hash, which proves the token was not altered — it does not hide anything. Putting a personal detail in a JWT payload because it "looks encrypted" publishes that detail to everyone who receives the token.

The other frequent mix-up is using a fast hash for passwords. SHA-256 is a good hash and a bad password store, because its speed — the property that makes it useful for checksums — is exactly what lets an attacker try billions of guesses. Password hashes are deliberately slow: Argon2id, scrypt, bcrypt.

Tools for this

Specifications this follows

Updated 2026-09-05