Dev.to Security 🔐 Cybersecurity 👁 0 📖 2 min read

Base64 is not encryption: what it is, when to use it, and the Unicode gotcha

Every few months I see Base64 described as "encoding for security." It isn't. Here's what it actually does. What Base64 is Base64 turns arbitrary bytes into text made only of safe characters (A–Z, a–z, 0–9, +

Every few months I see Base64 described as "encoding for security." It isn't. Here's what it actually does.

What Base64 is

Base64 turns arbitrary bytes into text made only of safe characters (A–Z, a–z, 0–9, +, /, plus = padding). That makes it possible to put binary data in places that only accept text: JSON, URLs, HTML, email.

Common uses:

  • Data URIs: data:image/png;base64,...
  • HTTP Basic auth headers
  • Parts of JWTs
  • Embedding small files in JSON APIs

What it is not

It's not encryption. There's no key and no secret. Anyone can decode it instantly:

atob("c2VjcmV0LXBhc3N3b3Jk"); // "secret-password"

If you see credentials "protected" with Base64, they're not protected.

It makes data bigger

Every 3 bytes become 4 characters, so output is about 33% larger. That's fine for small icons and tokens, but a bad idea for large files.

The Unicode gotcha

btoa only handles characters in the Latin-1 range:

btoa("héllo");  // works
btoa("नमस्ते");   // InvalidCharacterError
btoa("😀");      // InvalidCharacterError

The fix is to convert text to UTF-8 bytes first:

function toBase64(str) {
  const bytes = new TextEncoder().encode(str);
  let binary = "";
  bytes.forEach(b => (binary += String.fromCharCode(b)));
  return btoa(binary);
}

function fromBase64(b64) {
  const binary = atob(b64);
  const bytes = Uint8Array.from(binary, c => c.charCodeAt(0));
  return new TextDecoder().decode(bytes);
}

fromBase64(toBase64("नमस्ते 😀")); // "नमस्ते 😀"

Base64URL: the URL-safe variant

Standard Base64 uses + and /, which have special meaning in URLs. Base64URL swaps them for - and _ and usually drops the = padding. JWTs use this variant, so decoding one with plain atob can fail until you convert the characters back.

const toStandard = s => s.replace(/-/g, "+").replace(/_/g, "/");

Quick rules

  • Use it to move binary data through text-only channels
  • Don't use it to hide anything
  • Avoid it for large files
  • Always go through UTF-8 for non-English text

For quick checks while debugging, I use a Base64 encoder/decoder from the free tools on Erapse, a site I run. For anything sensitive, decode locally instead.

What's the strangest place you've found Base64 in the wild?

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.