Skip to main content
Security & Privacy

Hashing vs Encryption vs Encoding: What People Keep Confusing

Three transformations that all produce gibberish and solve different problems, with the measured cost of picking the wrong one for passwords.

By 9 min read
Article title card. Two pairs of boxes, the upper joined by a single amber arrow and the lower by arrows in both directions, with the line: one of these has no way back

Encoding, encryption and hashing all turn readable data into gibberish, which is why they get treated as interchangeable. They solve three unrelated problems, and picking the wrong one produces code that looks secure and is not.

One question separates them: do you ever need the original back, and if so, who is allowed to get it?

The three, side by side

The word hello put through Base64 encoding, AES-256-GCM encryption and SHA-256 hashing, showing the real output of each and who can recover the original
All three outputs look the same kind of unreadable. Only the middle one is protecting anything.
  • Encoding changes format so another system can carry the data. No key, reversible by anyone, zero security value.
  • Encryption hides data from anyone without the key. Reversible with it, which is the point.
  • Hashing produces a fixed-size fingerprint. One-way, because the original information is gone.

Encoding is a transport format, not a control

Data has to cross systems that only accept certain byte ranges, so you re-express bytes in a friendlier alphabet. Base64 maps onto 64 printable characters, hex onto two characters per byte, URL-encoding turns a space into %20.

There is no key and nothing to crack:

echo "aGVsbG8=" | base64 -d
hello

That is the entire attack. Confirm it with the Base64 Encode/Decode tool on any string you like.

Encoding is genuinely useful and genuinely not protective. HTTP basic auth is the clearest case: Authorization: Basic <base64> gets described as "encrypted credentials" in more design documents than it should, and it is a reversible transformation of a username and password. That is precisely why it must never travel outside TLS.

If your threat model includes someone who should not read the data, encoding does nothing at all.

Encryption, when you need it back

Plaintext plus a key gives ciphertext that is useless without that key. You reach for it whenever the original data is needed later: an OAuth refresh token in a database, a file going to storage you do not fully control, a session payload in a cookie.

Use an authenticated mode. AES-256-GCM produces both ciphertext and an authentication tag, so tampering fails loudly instead of decrypting to plausible garbage. Plain CBC without a separate MAC gives you no such signal, and that gap has produced real vulnerabilities. Our AES Encrypt/Decrypt tool runs GCM, CBC and CTR on text in the page, so you can watch a wrong passphrase produce nothing at all.

Symmetric means one key for both directions. AES lives here, it is fast, and the hard part is getting the key to the other party.

Asymmetric means a public and private pair, where the public half encrypts and only the private half decrypts. RSA and the elliptic-curve schemes live here, they are much slower, and they solve distribution. TLS uses asymmetric crypto only to agree on a symmetric key, then switches to AES for the traffic, which gets both properties.

The test: if you need to let someone encrypt without letting them decrypt, you need asymmetric.

Hashing produces a fingerprint, not a lockbox

A cryptographic hash maps any input to a fixed-size output, deterministically and one-way. SHA-256 returns 256 bits whether you feed it one letter or a four-gigabyte file.

"hello"  ->  2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
"Hello"  ->  185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969

One capital letter, an entirely unrelated output. That avalanche property is what makes hashing useful for integrity: hash a download, compare against the published digest, and a single changed byte is obvious. Our Hash Generator produces SHA-256 and friends locally.

On algorithm choice: MD5 and SHA-1 are finished for security purposes. SHA-1 collisions are not theoretical, they have been constructed: two distinct PDFs with the same digest3. Use SHA-256 or SHA-3. MD5 remains acceptable only where nobody is trying to fool you, such as detecting accidental file corruption.

A hash is not encryption, and you cannot decrypt one. The output is smaller than the input and there is no key. What an attacker can do is guess, hash the guess, and compare, and that is exactly where password code goes wrong.

The expensive mistake

The reasoning is seductive: hashing is one-way, so a hashed password is safe, so SHA-256 will do. It is a vulnerability, and the size of it is measurable.

On one RTX 5090, hashcat computes 28.4 billion SHA-256 hashes per second against about 9,525 bcrypt hashes per second at cost 10, a ratio of roughly three million to one
The green bar is drawn to scale. Three pixels is what a three-million-fold ratio looks like.

On a single RTX 5090, hashcat measures 28,353.3 MH/s against SHA-256 and 304.8 kH/s against bcrypt at cost 52. Bcrypt's cost is a power of two, so cost 10 is 32 times the work: about 9,525 guesses per second.

That is a ratio of roughly three million to one. A password that would survive four hours of attack against bcrypt falls in about five milliseconds against SHA-256, on the same card, in the same breach. Neither function is broken. One is fast on purpose, and that purpose is not password storage.

Plain hashing has a second problem: identical passwords produce identical hashes, so an attacker hashes a dictionary once and matches it against every leaked row simultaneously.

Salt

A random value, unique per user, mixed in before hashing. Two users with the same password now store different hashes, which kills precomputed tables and forces the attacker to work each account separately. The salt is not secret and is stored alongside the hash. Its only job is uniqueness.

A deliberately slow derivation function

Salt does not slow anything down, so the function itself has to. OWASP's current ranking1:

  • Argon2id is the first choice. Tunable on memory, time and parallelism, and the memory cost is what defeats GPUs and ASICs.
  • scrypt is the alternative when Argon2 is unavailable, also memory-hard.
  • bcrypt is explicitly for legacy systems where neither of the above exists. Worth knowing it silently ignores everything past the first 72 bytes.
  • PBKDF2 is not memory-hard and resists GPUs least well, but it is FIPS-approved, which is why regulated environments still use it. OWASP puts the work factor at 600,000 iterations for HMAC-SHA256. Our PBKDF2 Hash Generator shows how iterations and salt feed the derived output.

Note that bcrypt sits lower than most articles place it, including the earlier version of this one, which called it "older but still respectable" and listed it as a current good choice.

The model that keeps you out of trouble: a fast hash is for integrity, where speed is the feature. A slow derivation function is for passwords, where slowness is the entire product.

HMAC: a hash with a key

A plain hash proves a message was not altered accidentally. It proves nothing about who produced it, because anyone can recompute it, so anyone can change the message and recompute a matching digest.

HMAC mixes a secret key into the construction. Without the key you cannot produce a valid tag, so you cannot forge or alter the message undetected. This is what verifies webhook payloads and signed API requests: recompute the HMAC with your shared secret, compare, accept. Our HMAC Generator makes the dependency obvious, since changing one character of the key changes the whole tag.

HMAC gives integrity and authenticity, not confidentiality. The message is still readable. If it also needs hiding, encrypt it as well.

The decision table

I want toUseNot
Send binary through a text-only channelEncoding (Base64, hex)Anything called encryption
Hide data and read it back myselfAES-256-GCMHashing, encoding
Let others encrypt to me without a shared secretRSA or ECCSymmetric
Store user passwordsArgon2id, or scryptSHA-256, MD5, encoding, encryption
Check a download was not alteredSHA-256A slow KDF
Prove a message came from a trusted senderHMAC-SHA-256A plain hash
Detect accidental file corruptionCRC32, even MD5A slow KDF

Five ways this goes wrong

Each of these is a failure mode with a name, and each follows from picking a tool by how its output looks rather than by what it does.

  1. Base64 in a column called encrypted_password. One database export and every password is plaintext, because nothing was ever encrypted.

  2. Unsalted MD5 for passwords. Fast and collision-broken, and both problems compound. Leaked hashes match precomputed tables almost immediately.

  3. Encrypting passwords instead of hashing them. Done so logins can be "verified" by decrypting and comparing. The system can now recover every plaintext password, which is the outcome you were trying to prevent. You never need a password back, only a yes-or-no on a login attempt, and the existence of a decrypt path is itself the bug.

  4. Hashing data you will need later. The mirror image. Account numbers get SHA-256'd, then someone needs to display them, and the data is gone. That was an encryption job all along.

  5. A plain hash where HMAC belonged. A webhook endpoint hashes the body and compares it against a header, with no secret involved. Anyone can compute that hash, so anyone can forge the request. The key is the entire point.

Summary

Encoding reformats and protects nothing. Encryption hides data from anyone without the key, and is what you want whenever the original is needed later. Hashing gives a one-way fingerprint for integrity and verification.

Passwords are the case where the wrong choice is most expensive and most measurable: salt them, run them through Argon2id, and understand that reaching for SHA-256 instead multiplies your attacker's throughput by roughly three million.

Pick by the problem, not by how the gibberish looks.

Sources

Every number in this article traces to a source below. Where a claim could not be sourced, it was cut rather than softened.

  1. Primary sourceOWASP

    That Argon2id is the recommended first choice for password hashing, that bcrypt should only be used in legacy systems where Argon2 and scrypt are unavailable, and the recommended PBKDF2-HMAC-SHA256 work factor of 600,000 iterations.

  2. Primary sourceChick3nman

    The measured single-card rates of 28,353.3 MH/s against SHA2-256 and 304.8 kH/s against bcrypt at cost 5, from which the cost 10 figure is derived.

  3. Primary sourceCWI Amsterdam and Google Research

    That a practical collision for full SHA-1 has been demonstrated, producing two distinct PDF files with the same SHA-1 digest.

Topics

Tools mentioned in this article

Get new tools by email

New tools and the occasional deep-dive, about once a month. No spam, no sharing your address, unsubscribe in one click.

Related articles

Article title card. Two rows of cipher blocks on a dark grid, the lower row ending in an amber block labelled tag, with the line: one mode authenticates, one does not
Security & Privacy

AES-GCM vs AES-CBC: Which Mode to Use and Why

What block cipher modes do, why GCM replaced CBC for new code, what nonce reuse actually leaks, and the NIST limits that make it a requirement rather than advice.

Article title card. A shield with its right half filled in amber, with the line: what leaves the machine, and when
Security & Privacy Guide

Online Privacy Guide: Threat Models for Browser-Based Work

Threat models worth naming, the thirty-second check that beats any privacy policy, and an honest list of which of our own tools upload your file.

A pink sticky note handwritten with the word password and the digits 123456, stuck to a black laptop keyboard against a blue background
Security & Privacy

Password Strength: What Length and Character Sets Buy You

Entropy done honestly, NIST's current 15-character floor, and measured GPU cracking speeds showing the hash function matters more than the extra characters.