Skip to content
100% in your browser. Nothing you paste is uploaded — all processing runs locally. Read more →

Base64 isn’t encryption — encoding vs hashing vs encrypting

On this page
  1. Encoding just changes representation
  2. Why Base64 feels “secret” (and why it isn’t)
  3. Encoding vs hashing vs encryption (plain comparison table)
  4. Where engineers still misuse Base64 in production
  5. Encode/decode safely in your browser

Base64 looks scrambled. That is why people treat it like a lock. It is not.

Base64 is encoding: a reversible alphabet swap so binary can travel through text-only systems. Anyone with the string can recover the original bytes. No key. No secret. atob() in a browser console is enough.

Hashing and encryption are different jobs. Hashing is one-way. Encryption needs a key. This page pulls the three apart, shows why Base64 still gets misused in production, and points at the browser encoder if you just need to transform text — nothing is uploaded, and there is no account.

Encoding just changes representation

Encoding answers one question: how do I write these bytes so a text system will carry them? Email, HTTP headers, JSON strings, and YAML fields were built for characters, not arbitrary bytes. Base64 maps every 3 input bytes to 4 characters from a 64-character alphabet (A–Z, a–z, 0–9, +, /, plus = padding). The URL-safe variant swaps + and / for - and _. Same bytes, different surface.

The mapping is public and fixed in RFC 4648. There is no passphrase. Decode is the inverse of encode. SGVsbG8= is Hello. Always.

That is useful. It is also the whole trick. Encoding does not hide, shrink, or authenticate. It adds about 33% size. If you need a primer on the alphabet and padding, see What is Base64?. If you need the URL-safe form, see Base64 vs Base64url.

A quick test: give the output to someone with zero secrets. If they can recover the input, you encoded. You did not encrypt.

Why Base64 feels “secret” (and why it isn’t)

Base64 output does not look like English. c3VwZXJzZWNyZXQxMjM= does not shout supersecret123. That visual noise is why it gets treated as obfuscation.

It is a costume, not a lock. The decoder is in every language stdlib, every browser, and most terminals:

echo c3VwZXJzZWNyZXQxMjM= | base64 -d
# supersecret123

No key material. No rate limit. No authorized decoder. If a value is in a repo, a YAML file, a cookie, or a URL, anyone who can read it can decode it.

“Looks unreadable” is not a security property. Security properties are confidentiality (only the intended party can read it), integrity (tampering is detectable), and authenticity (you know who produced it). Base64 provides none of those. It provides transportability.

If the plan is “hide this from someone who can see the string,” Base64 is the wrong tool. They will paste it into the decoder on this page — or their own console — and be done.

Encoding vs hashing vs encryption (plain comparison table)

Three operations get collapsed in Slack threads. They are not interchangeable.

Encoding (Base64)HashingEncryption
JobChange representationFingerprint the inputHide the input
Reversible?Yes, no keyNo (one-way)Yes, with the key
Needs a secret?NoNo (unless HMAC)Yes — a key
Same input →Same outputSame digestCiphertext (plus IV/nonce)
Size~33% largerFixed (e.g. 32 bytes for SHA-256)About the same as input + overhead
Typical useMIME, data URIs, text-safe blobsIntegrity, passwords (slow KDFs), checksumsSecrets at rest or in transit
If leakedAttacker has the originalAttacker has a digest, not the inputAttacker has ciphertext; needs the key

Encoding is for pipes that refuse raw bytes.

Hashing is for “prove this is the same data” or “store a password without storing the password.” SHA-256 is fine for integrity. Passwords need a slow, salted KDF — argon2id, bcrypt, or scrypt. The decision tree is on hash.tooljo.com and which hash function to use.

Encryption is for “only the holder of this key may read it.” AES-GCM, libsodium, age, or a cloud KMS. The key is the security. The encoding of the ciphertext (often Base64) is just how you ship the bytes.

You can encrypt and then Base64 the ciphertext so it fits in JSON. The privacy came from the key, not the alphabet.

Where engineers still misuse Base64 in production

These show up in real code reviews. Same root mistake: treating “looks scrambled” as “is secret.”

API keys in client bundles. const KEY = atob("c2tfbGl2ZV8uLi4=") hides nothing. The decoded key still goes out in network requests. Client-side code cannot hold a secret. Use a server you control, or a key designed for public exposure (scoped, rotatable, low-trust).

Passwords stored as Base64. A database dump plus atob is the password list. Store a slow hash (argon2id), not an encoding. See the hash guide.

Kubernetes Secret objects. The data: field is Base64 by spec. Anyone with get secrets can decode the values. Enable etcd encryption, tighten RBAC, and treat “can read Secrets” as “knows the plaintext.” For higher sensitivity, use an external secret manager. (A longer walkthrough is on the Kubernetes secrets post.)

PII in URLs. ?u=eyJlbWFpbCI6ImFsaWNlQGV4YW1wbGUuY29tIn0= is still Alice’s email. URLs get logged, previewed, and forwarded. Do not put personal data in query strings — encoded or not.

“Hidden” cookies and config. Base64-encoded JSON in a cookie ({"role":"user"}) can be decoded, edited, and re-encoded. Sign the cookie or use a server-side session. A .env value that is Base64 of a key is still the key.

When Base64 is right: data URIs, MIME attachments, binary-in-JSON, HTTP Basic Auth under HTTPS, and any wire format that needs ASCII. Those are transport problems. More case studies live on the older blog post.

Encode/decode safely in your browser

If you need to encode or decode a string, do it locally.

The Base64 encoder/decoder runs in your browser with the built-in btoa/atob APIs plus UTF-8 handling. Paste on either side; the other updates. Toggle URL-safe and padding as needed. Nothing is uploaded. There is no signup.

Use it to confirm the first rule of this page: take any “secret-looking” Base64 string you are about to ship, decode it here, and read the plaintext. If that worries you, you wanted a hash or a real encrypt — not an encoding.

Open the encoder →