Three terms that get mixed up constantly. A developer who confuses them will choose the wrong tool — storing passwords with encryption instead of hashing, or using Base64 where they need AES. This post untangles all three.
The Short Answer
| Reversible? | Requires a key? | Common use | |
|---|---|---|---|
| Encoding | Yes | No | Transmission (Base64, URL encoding) |
| Encryption | Yes | Yes | Securing data (AES, RSA, TLS) |
| Hashing | No | No | Verification (passwords, checksums) |
Encoding: Change the Format, Not the Meaning
Encoding transforms data into a different format so it can travel through a system that only accepts a specific character set. It is purely cosmetic — it changes how the data looks, not what it means.
Base64 is the most common encoding. It takes binary data and represents it using only 64 printable ASCII characters (A–Z, a–z, 0–9, +, /). Used in:
- Embedding images in CSS/HTML as data URIs
- HTTP Basic Auth headers (
username:password→YWRtaW46c2VjcmV0) - JWT tokens (each section is Base64url-encoded)
- Email attachments (MIME encoding)
// Encoding — no key, trivially reversible
btoa("admin:secret"); // "YWRtaW46c2VjcmV0"
atob("YWRtaW46c2VjcmV0"); // "admin:secret"
URL encoding converts characters that aren't safe in URLs (spaces, &, =, etc.) to percent-encoded form: hello world → hello%20world.
Critical point: Encoding provides zero security. Anyone can decode Base64 or URL-encoded strings instantly — there's no secret involved. If you see Base64 in a JWT, the payload is readable to anyone who sees the token.
Encryption: Transform Data So Only Authorised Parties Can Read It
Encryption uses a key to scramble data into ciphertext. Without the correct key, the ciphertext is meaningless. With it, you can reverse the process and get the original data back.
Two broad types:
Symmetric encryption (one key for both directions)
// AES-GCM in the Web Crypto API
const key = await crypto.subtle.generateKey(
{ name: "AES-GCM", length: 256 },
true,
["encrypt", "decrypt"]
);
const plaintext = new TextEncoder().encode("hello");
const iv = crypto.getRandomValues(new Uint8Array(12));
const ciphertext = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv },
key,
plaintext
);
const decrypted = await crypto.subtle.decrypt(
{ name: "AES-GCM", iv },
key,
ciphertext
);
new TextDecoder().decode(decrypted); // "hello"
AES-256 is the standard for symmetric encryption. Used for: encrypting files, databases, HTTPS session data.
Asymmetric encryption (public key encrypts, private key decrypts)
RSA and elliptic curve (ECDSA/ECDH) are common asymmetric algorithms. Used for: TLS handshakes, email encryption (PGP), code signing.
Key management is the hard part. Encryption security depends entirely on keeping keys secret. Lose the key, lose the data. Leak the key, the "encrypted" data is now public.
Hashing: One-Way Transformation
A hash function takes any input and produces a fixed-length output (the hash or digest). The key property: it cannot be reversed. You cannot get the original data back from a hash alone.
// SHA-256 in Web Crypto API
async function sha256(text) {
const encoded = new TextEncoder().encode(text);
const buffer = await crypto.subtle.digest("SHA-256", encoded);
return [...new Uint8Array(buffer)]
.map(b => b.toString(16).padStart(2, '0'))
.join('');
}
await sha256("hello");
// "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
await sha256("hello");
// "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
// Identical inputs always produce identical outputs
await sha256("Hello"); // capitalised
// "185f8db32921bd46d35fa8a7d5c5765f18b3f28a15e4b46d2ec9ef7f4ebbf09b"
// Completely different hash for a tiny input change
Where hashing is used
Password storage: Never store passwords in plain text or encrypted form. Store the hash. When a user logs in, hash their input and compare to the stored hash.
// Use bcrypt, Argon2, or scrypt for passwords (not raw SHA-256!)
// These add "work factor" to slow down brute-force attacks
const bcrypt = require("bcrypt");
const hash = await bcrypt.hash("user_password", 12); // 12 = cost factor
const match = await bcrypt.compare("user_password", hash); // true
File integrity: Hash a file and publish the hash. Users download the file, hash it themselves, and compare. If the hashes match, the file wasn't corrupted or tampered with.
Content-addressed storage: Git commits are SHA-1 hashes of their content. Change one byte → completely different hash → git knows something changed.
Data deduplication: Hash file contents; if you've seen that hash before, you've seen that file before.
Common hash algorithms
| Algorithm | Output size | Status |
|---|---|---|
| MD5 | 128 bits (32 hex chars) | Broken — collisions found, do not use for security |
| SHA-1 | 160 bits (40 hex chars) | Weak — avoid for new systems |
| SHA-256 | 256 bits (64 hex chars) | Secure — standard for general use |
| SHA-512 | 512 bits (128 hex chars) | Secure — higher security margin |
| bcrypt | 60 chars (includes salt) | Recommended for passwords |
| Argon2 | Variable | Best current choice for passwords |
Why MD5 and SHA-1 are broken: Researchers have found ways to produce two different inputs that generate the same hash (a "collision"). A malicious file can be crafted to have the same MD5 hash as a legitimate file. For checksums and non-security uses (cache keys, content IDs), MD5 is still commonly used. For anything security-related — don't.
The Biggest Mistakes
1. Encoding passwords instead of hashing them
Base64 encoding a password is trivially reversible. SHA-256 without a salt is vulnerable to rainbow table attacks. Use bcrypt, Argon2, or scrypt — they're designed for passwords.
2. Encrypting passwords instead of hashing them
If you encrypt passwords, the encryption key becomes the crown jewel of your security. If the key leaks, all passwords are exposed simultaneously. Hashing means even if your database leaks, each password needs to be cracked individually.
3. Using hashing to hide data that needs to be recoverable
Hashes are one-way. If you need to show a user their original data back, use encryption — not hashing.
4. Checking "is this string Base64?" as a security measure
Base64 is not a security primitive. A string that looks like Base64 may contain anything. Validate what the decoded content is, not the encoding.
Quick Decision Guide
Need to transmit binary data as text? → Base64 encoding
Need to secure data that must be recovered later? → AES encryption
Need to store passwords? → bcrypt or Argon2 (not SHA-256!)
Need to verify file integrity? → SHA-256 hash
Need a fast, non-security hash (cache key, dedup)? → SHA-256 or MD5
To generate SHA-256, SHA-512, MD5, and SHA-1 hashes from any text string — instantly in your browser — the SnappyTools Hash Generator does all four at once with no installation or signup. Useful for checksums, debugging, and learning which algorithm produces which format.
Top comments (0)