[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.
// 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-8 —
btoa('héllo')lanza una excepción en el navegador porqueéestá fuera de Latin-1. UsaTextEncoderprimero 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