DEV Community

Naveen Hooda
Naveen Hooda

Posted on

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, +, /, 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"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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("नमस्ते 😀")); // "नमस्ते 😀"
Enter fullscreen mode Exit fullscreen mode

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, "/");
Enter fullscreen mode Exit fullscreen mode

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?

Top comments (0)