[GUÍA] 9 min read

[GUÍA] ¿Qué es Base64 y cómo funciona?

Una explicación práctica desde los primeros principios. Aprende el truco del agrupamiento de 6 bits detrás de Base64, por qué la salida es 33% más grande, de dónde viene el relleno =, y cuándo usarlo.

April 2026 | fundamentals

// LA DEFINICIÓN EN UNA ORACIÓN

Base64 es una representación textual de datos binarios que utiliza únicamente 64 caracteres ASCII imprimibles: A–Z, a–z, 0–9, y dos símbolos (típicamente + y /, con = como marcador de relleno). Esa es toda la idea. Cada tres bytes de binario se reagrupan en cuatro caracteres Base64, y nada más cambia.

Es una codificación, no un cifrado — cualquiera puede revertirla en milisegundos. El propósito no es la privacidad; es el transporte seguro. Base64 te permite colocar cargas binarias (imágenes, certificados, archivos PDF, claves criptográficas) en lugares que solo aceptan texto: atributos HTML, cadenas JSON, URLs, cuerpos de correo electrónico, configuraciones YAML, columnas TEXT de bases de datos, variables de entorno.

// POR QUÉ LO NECESITAMOS

Los canales solo de texto eliminan o mutan los bytes que no reconocen. Una pasarela de correo electrónico que entiende solo ASCII de 7 bits romperá el bit alto de cada carácter no inglés. Un analizador JSON rechazará bytes nulos incrustados. Una URL que contiene un espacio literal, comilla o ampersand es inválida hasta que esos caracteres se codifiquen como porcentaje. Los estándares SMTP de los 90 asumían que los cuerpos de correo siempre serían texto plano en inglés — pero la gente también quería enviar fotos y hojas de cálculo.

Base64 resuelve esto restringiendo la salida a 64 caracteres que sobreviven intactos a cualquier canal de texto conocido: 26 letras mayúsculas, 26 letras minúsculas, 10 dígitos, y dos símbolos que son (a) imprimibles, (b) no utilizados por los mecanismos comunes de escape, y (c) suficientemente distintos para ir y volver por ASCII. Más un carácter de relleno =, que también es seguro.

// EL TRUCO DEL AGRUPAMIENTO DE 6 BITS

Este es el corazón mecánico de Base64 en una línea: toma la entrada como un flujo de bits, córtala en piezas de 6 bits y busca cada pieza en un alfabeto de 64 caracteres.

¿Por qué 6 bits? Porque 2^6 = 64, así que cada fragmento de 6 bits se asigna exactamente a un carácter del alfabeto. ¿Por qué no 7 u 8 bits? 7 bits da 128 valores (demasiados — no todos son imprimibles), y 8 bits es de nuevo binario crudo. 6 bits es el punto óptimo donde cada valor posible tiene un carácter legible por humanos.

Tres bytes de entrada = 24 bits = exactamente cuatro grupos de 6 bits = cuatro caracteres Base64. Esa proporción 3-entrada/4-salida es fija; es la razón por la que la salida Base64 es ~4/3 del tamaño de la entrada, es decir, aproximadamente 33% más grande.

// Worked example: encoding the 3 bytes 'Man' (77 97 110)
// ASCII:  M        a        n
// Binary: 01001101 01100001 01101110
// Re-group into 6-bit chunks:
//         010011 010110 000101 101110
// Decimal: 19     22     5      46
// Base64:  T      W      F      u
// Result:  'TWFu'  (stored as 4 ASCII bytes: 84 87 70 117)

// EL ALFABETO BASE64

RFC 4648 §4 define el alfabeto estándar. Las posiciones 0–25 son A–Z, 26–51 son a–z, 52–61 son 0–9, la posición 62 es +, y la posición 63 es /.

La variante URL-safe (RFC 4648 §5, también conocida como base64url) intercambia + por - y / por _, de modo que la salida se puede colocar de forma segura en URLs y nombres de archivo sin más escape. Ambas variantes se decodifican de forma idéntica si aplicas el alfabeto correspondiente.

// Standard Base64 alphabet (index → character)
// 0–25:  A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
// 26–51: a b c d e f g h i j k l m n o p q r s t u v w x y z
// 52–61: 0 1 2 3 4 5 6 7 8 9
// 62:    +       (or  -  in URL-safe)
// 63:    /       (or  _  in URL-safe)
// pad:   =

// DE DÓNDE VIENE EL RELLENO =

El algoritmo espera tamaños de entrada que sean múltiplos de 3 bytes (para que se agrupen uniformemente en 4 caracteres Base64). Cuando la longitud de entrada no es múltiplo de 3, sobran uno o dos bytes, lo que no es suficiente para rellenar el grupo final de 4 caracteres de salida. El relleno con = indica cuántos bytes faltaban.

• Longitud de entrada mod 3 == 0 → sin relleno (p. ej., 'Man' → 'TWFu').
• Longitud de entrada mod 3 == 1 → dos caracteres de relleno (p. ej., 'M' → 'TQ==').
• Longitud de entrada mod 3 == 2 → un carácter de relleno (p. ej., 'Ma' → 'TWE=').

// Why 'M' becomes 'TQ==':
// Byte: M = 01001101                   (8 bits)
// Padded to 12 bits with zeros: 010011 010000
// Decimal: 19, 16  →  'T', 'Q'
// Output length must be multiple of 4 → add '==' padding
// Result: 'TQ=='
//
// When decoding, the decoder strips '==' and recovers the first 8 bits,
// discarding the trailing zero bits.

// ¿ES EXACTO EL SOBRECOSTO DEL 33%?

Cerca, pero no exactamente. La fórmula exacta es ceil(n / 3) × 4 caracteres para una entrada de bytes de longitud n. Para una entrada de 100 KB, eso es ceil(102400 / 3) × 4 = 136,534 caracteres — 33.3% más que 100 KB. En la práctica, una vez que agregas compresión gzip o Brotli a nivel HTTP, el costo por cable está más cerca del 10–15%, porque el texto Base64 se comprime razonablemente bien (aunque no tan bien como el binario crudo).

Para cargas pequeñas el relleno importa: codificar 1 byte cuesta 4 caracteres (cuatro veces el tamaño), y codificar 2 bytes también cuesta 4 caracteres. Así que las cadenas Base64 diminutas se ven sorprendentemente largas comparadas con sus entradas.

// DÓNDE SE USA BASE64 EN EL MUNDO REAL

  • > Data URIs en HTML/CSS — incrusta íconos y logos en línea: <img src="data:image/png;base64,…">
  • > Cargas JSON y GraphQL — APIs que necesitan enviar binario (subidas de archivos, miniaturas) dentro de un protocolo de texto
  • > Tokens JWT — las tres partes de un JWT separadas por puntos son cadenas base64url
  • > HTTP Basic Auth — usuario:contraseña se codifica en Base64 en el encabezado Authorization (¡sigue sin ser seguro sin TLS!)
  • > Correo electrónico (MIME) — los adjuntos se codifican en Base64 para sobrevivir a los servidores SMTP heredados de 7 bits
  • > Certificados y claves en formato PEM — el bloque -----BEGIN CERTIFICATE----- envuelve un blob DER codificado en Base64
  • > Claves SSH — las líneas de id_rsa.pub y authorized_keys son claves públicas codificadas en Base64
  • > Columnas TEXT de base de datos — cuando no puedes usar un tipo BLOB, Base64 te permite almacenar binario como texto
  • > Variables de entorno — los valores de los Secrets de Kubernetes están codificados en Base64 (aunque NO cifrados)
  • > Códigos QR y enlaces mágicos — tokens Base64 cortos seguros para incrustar en URLs

// BASE64 NO ES CIFRADO

Esta es la confusión más común. Base64 no oculta el contenido de tus datos — reescribe el binario como texto usando una asignación pública y bien documentada. Una contraseña codificada en Base64 como cGFzc3dvcmQ= es exactamente tan vulnerable como el texto plano password; cualquiera puede decodificarla con una sola llamada a función. Trata a Base64 como tratarías al hexadecimal: es una representación, no protección.

Si necesitas privacidad, aplica primero una primitiva criptográfica real (AES-GCM, ChaCha20-Poly1305, age, PGP) al texto plano, y luego codifica en Base64 el texto cifrado si necesitas transportarlo a través de un canal de texto. El paso Base64 es la última milla, no la capa de seguridad.

Los Secrets de Kubernetes son la trampa canónica: Kubernetes almacena los valores secretos codificados en Base64, lo que los hace parecer vagamente protegidos. No lo están — cualquiera con acceso de lectura al namespace puede revertirlos. La verdadera protección de secretos requiere herramientas como SealedSecrets, Vault, SOPS, o integraciones KMS nativas de la nube.

// UN CODIFICAR / DECODIFICAR RÁPIDO EN CADA LENGUAJE

// JavaScript (browser):
//   btoa('Hello')            → 'SGVsbG8='
//   atob('SGVsbG8=')         → 'Hello'
//   (btoa/atob only handle Latin-1; use TextEncoder for Unicode)
//
// Node.js:
//   Buffer.from('Hello').toString('base64')     → 'SGVsbG8='
//   Buffer.from('SGVsbG8=', 'base64').toString() → 'Hello'
//
// Python:
//   import base64
//   base64.b64encode(b'Hello')       → b'SGVsbG8='
//   base64.b64decode('SGVsbG8=')     → b'Hello'
//
// Go:
//   base64.StdEncoding.EncodeToString([]byte("Hello")) → "SGVsbG8="
//
// Ruby:
//   Base64.strict_encode64('Hello')   → 'SGVsbG8='
//
// Shell:
//   echo -n 'Hello' | base64          → 'SGVsbG8='
//   echo 'SGVsbG8=' | base64 -d        → 'Hello'

// ERRORES COMUNES A EVITAR

  • > Codificar una cadena URL-safe con el alfabeto estándar — el decodificador del receptor rechazará los + y /. Usa base64url cuando la salida va a una URL, cookie o nombre de archivo.
  • > Doble codificación — pasar texto ya codificado en Base64 por el codificador nuevamente. Siempre verifica si los datos ya están en Base64 antes de codificar.
  • > Olvidar UTF-8btoa('héllo') lanza una excepción en el navegador porque é está fuera de Latin-1. Usa TextEncoder primero para convertir la cadena en bytes.
  • > Desajustes en el recorte de relleno — base64url a menudo omite el relleno =. Si tu decodificador es estricto, vuelve a agregar relleno: input + '==='.slice((input.length + 3) % 4).
  • > Asumir que Base64 es seguro — no lo es. El cifrado es separado. Siempre.
  • > Usar Base64 para binarios enormes — codificar un archivo de 500 MB en una sola cadena agotará la memoria. En su lugar, procésalo en flujo.
  • > Envuelto en líneas vs sin envolver — MIME/PEM envuelven Base64 en 64 o 76 columnas con \n. La mayoría de los otros contextos esperan una sola línea. Elimina o inserta saltos de línea según corresponda.

// BASE64 EN 30 SEGUNDOS — LA HOJA DE TRUCOS

  • > 64 caracteres ASCII imprimibles (A–Z, a–z, 0–9, +, /) más relleno =
  • > 3 bytes de entrada → 4 caracteres de salida (proporción fija)
  • > La salida es ~33% más grande que la entrada
  • > Reversible: es codificación, no cifrado
  • > Usa base64url (reemplaza + → -, / → _) para URLs y nombres de archivo
  • > Rellena entradas cortas con 1 o 2 caracteres = para que la longitud de salida sea múltiplo de 4
  • > Para texto Unicode: codifica la cadena a bytes UTF-8 primero, luego Base64 esos bytes
  • > Decodificable por la biblioteca estándar de todo lenguaje mayoritario
  • > Seguro en HTML, JSON, URLs, correos electrónicos y archivos PEM
  • > No es seguro como capa de seguridad — siempre combínalo con cifrado real para datos sensibles

// PRÓXIMOS PASOS

Ahora que conoces la mecánica, prueba nuestro codificador Base64 o decodificador Base64 e inspecciona la salida carácter por carácter. Para cargas URL-safe, cambia a base64url. Para imágenes, consulta imagen → Base64.

Profundizaciones relacionadas:
Base64 URL-safe vs estándar — cuándo usar cada alfabeto
Base64 para UTF-8 y Unicode — evitando la trampa de btoa()
Base64 en JavaScript & Node.js — atob, btoa, Buffer
Base64 vs Base64URL vs Base32 — tabla comparativa