DevKitLab Logo DevKitLab
Base64 / Codificación / Depuración

¿Qué es Base64 y por qué no se decodifica el mío?

Pegas una cadena Base64 en un decodificador y devuelve «Invalid character» —o peor, funciona y te entrega una pantalla de galimatías—. Casi nunca hay nada corrupto. Has cometido un error de categoría sobre qué es Base64: ni cifrado ni compresión, solo una forma reversible de llevar bytes por canales de solo texto —y en cuanto lo ves, cada fallo de decodificación se vuelve un elemento de una lista corta y concreta.

Pegas una cadena Base64 en un decodificador y te devuelve Invalid character. O peor —funciona, y te entrega una pantalla entera de galimatías—. Así que pruebas otra herramienta y sí decodifica; pruebas el atob() del navegador y lanza una excepción; decodificas en la línea de comandos y obtienes algo sutilmente distinto otra vez. A estas alturas sospechas que la cadena está corrupta y te vas a buscar dónde se truncó. Casi siempre, no hay nada corrupto. Solo has cometido un error de categoría sobre qué es Base64.

Aquí está el replanteamiento: Base64 no es cifrado, ni compresión, ni una suma de verificación. Es una forma reversible de reempaquetar bytes arbitrarios en 64 caracteres que sobreviven a canales de solo texto. Responde a una sola pregunta —«¿cómo llevo bytes en bruto por algo que solo acepta texto?»— y no promete nada sobre secreto (cualquiera puede decodificarlo) ni sobre tamaño (de hecho agranda los datos alrededor de un tercio). En cuanto lo tienes, «¿por qué no decodifica?» deja de ser un misterio y se descompone en una lista corta de desajustes concretos: un alfabeto distinto, padding que falta, un espacio que antes era un +, o bytes que se decodificaron perfectamente y solo parecen galimatías porque nunca fueron texto.

Este artículo dedica unos minutos a qué es Base64 de verdad —tres bytes entran, cuatro caracteres salen— y luego recorre cada razón común por la que una decodificación falla o engaña, desde la variante de alfabeto cambiado que hace tropezar a muchos tokens decodificados a mano hasta la trampa de «decodificó bien, solo que no es texto», que no es en realidad un fallo. Al terminar tendrás una lista para cualquier cadena que no se deje abrir.

La única idea: un reempaquetado de bytes a texto, no una caja fuerte

Mantén dos cosas separadas y casi toda la confusión se despeja. Base64 transforma bytes en texto y de vuelta —sin pérdida y sin clave—. Existe porque muchísimos canales están definidos para texto y estropearán o rechazarán el binario en bruto: una URL, una cabecera HTTP, una cadena JSON, el cuerpo de un correo, un data: URI. Cuela un byte nulo o un carácter de bit alto en cualquiera de ellos y se rompe. Base64 lava el binario arbitrario en un subconjunto seguro de 64 caracteres —A–Z, a–z, 0–9, +, /— que pasa por la mayoría de ellos prácticamente intacto. (Un matiz: los propios +, / y = del Base64 estándar aún tienen significado en la query de una URL o en un formulario HTML, así que esos dos casos en concreto piden la variante URL-safe o una ronda extra de codificación URL —el detalle detrás del Fallo 3 de abajo.)

Dos consecuencias que conviene grabar, porque de ahí sale la mitad de los malentendidos:

  • Es público. La transformación la revierte cualquiera, sin ningún secreto de por medio. Codificar en Base64 una contraseña o una API key no la protege —solo la hace algo menos obvia para quien echa un vistazo—. Si necesitas secreto, necesitas cifrado; Base64 es un disfraz, no un candado.
  • Es más grande. Cada 3 bytes se vuelven 4 caracteres —cerca de un 33% de sobrecarga—. Es lo contrario de comprimir. Hay quien lo usa esperando encoger datos; hace lo inverso.

Cómo funciona de verdad: tres bytes entran, cuatro caracteres salen

El mecanismo merece treinta segundos, porque explica casi todos los fallos. Base64 corta la entrada en grupos de 3 bytes (24 bits), luego recorta cada 24 bits en cuatro números de 6 bits, y asigna cada valor de 6 bits (0–63) a un carácter del alfabeto. Tres bytes siempre se vuelven cuatro caracteres:

Man  →  01001101 01100001 01101110   (3 bytes = 24 bits)
        010011 010110 000101 101110  (cuatro grupos de 6 bits)
        T      W      F      u          → "TWFu"

Cuando la entrada no es múltiplo de 3 bytes, el grupo final queda corto y Base64 rellena la salida hasta un múltiplo de 4 con =: un byte sobrante se vuelve dos caracteres y ==, dos bytes sobrantes se vuelven tres caracteres y =. Ese = no es dato —es alineación—. Por eso la longitud de una cadena Base64 estándar es siempre múltiplo de cuatro, y por eso los fallos de abajo se agrupan en torno a exactamente tres cosas: el alfabeto, el padding y la longitud.

Fallo 1: es Base64URL, no Base64

Un fallo de decodificación muy común. Base64URL es una variante construida para sobrevivir a URLs y cabeceras HTTP, y un decodificador estándar se atraganta con ella. Dos diferencias importan:

  • + se vuelve -, y / se vuelve _.
  • El padding = final suele quitarse.

(Si el texto se parte en líneas de ancho fijo es una decisión aparte, de nivel de transporte —no forma parte del alfabeto Base64URL—, aunque las cadenas URL-safe se envían por convención sin partir.) Lo cruel es que + y / son justo los caracteres con más probabilidad de aparecer en un hash, una clave o una firma —así que la corrupción es común y silenciosa—. Es una trampa muy habitual al decodificar JWT a mano: los tres segmentos de un token son Base64URL, así que pasar uno a un decodificador estándar lanza una excepción o devuelve basura —un desajuste que descarrila muchos intentos de «voy a decodificar mi token». El arreglo es traducir primero —cambia -/_ de vuelta a +// y vuelve a rellenar hasta un múltiplo de cuatro— o usar un decodificador con modo URL-safe. En una herramienta Base64, pasa la misma cadena por el modo URL-safe frente al estándar y observa cómo una lee limpio mientras la otra falla con la mismísima entrada.

Fallo 2: el padding

El padding es la otra mitad de esa historia. Una cadena Base64 cuya longitud no es múltiplo de cuatro puede que solo tenga el padding = incompleto —o puede estar mal formada o truncada— y los decodificadores no se ponen de acuerdo. Los estrictos lanzan Invalid length; los tolerantes infieren el padding que falta y funcionan. Ese desacuerdo es justo por qué la misma cadena «decodifica» en una herramienta y falla en otra, lo que manda a la gente a perseguir una corrupción que no existe. La longitud módulo cuatro te dice en qué caso estás: un resto de 0 ya está completo; 2 significa añadir ==; 3, añadir un solo =; y un resto de 1 no es un problema de padding en absoluto —es una cadena ilegal o truncada, porque un grupo Base64 válido nunca deja exactamente un carácter de más—. (El padding solo aparece al final, como mucho dos =, y nunca en medio —un = en mitad de la cadena es en sí una señal de corrupción.) Y como Base64URL quita el padding a propósito, este fallo y el Fallo 1 suelen viajar juntos: una cadena URL-safe está a la vez recodificada en letras y sin padding.

Fallo 3: un espacio que antes era un +

Este es el más escurridizo, y donde este artículo enlaza con el siguiente. Si una cadena Base64 viajó por la query de una URL o un formulario HTML, sus + pueden haberse convertido en espacios —porque en application/x-www-form-urlencoded, un + significa espacio—. Ahora tu decodificador ve un donde iba un + y o falla o produce los bytes equivocados. Los saltos de línea intrusos hacen el mismo daño: algunos formatos MIME o PEM insertan saltos de línea (habitualmente cada 64 o 76 caracteres, según el formato), y esos saltos (como cualquier espacio de copiar y pegar) no son parte de los datos. El arreglo depende de su origen, y el orden importa: primero decide si pasó por codificación de formulario —si es así, restaura como + los espacios que la decodificación de formularios convirtió originalmente desde +— y, por separado, quita los saltos de línea y el espacio de copiar y pegar que insertó el transporte. No conviertas todos los espacios a + sin pensar; solo los codificados por formulario fueron alguna vez signos más. En realidad es un problema de codificación URL con máscara de Base64. Base64URL esquiva toda esta clase de bug al no usar + en absoluto.

Fallo 4: decodificó bien —solo que no es texto

El fallo que no es un fallo. A veces la decodificación funciona y aun así obtienes galimatías, así que concluyes que la cadena era mala. No lo era. Los bytes decodificados no tienen por qué ser texto legible. Base64 transporta bytes arbitrarios: el original pudo ser un PNG, un flujo gzip, un mensaje protobuf o un blob cifrado, y renderizar esos bytes en bruto como texto produce exactamente el galimatías que cabría esperar. Incluso cuando el contenido sí es texto, es texto en alguna codificación —decodifica bytes UTF-8 como Latin-1 y café vuelve como café—. Así que antes de dar una decodificación por «fallida», pregunta qué son los bytes; los primeros suelen anunciarlo (\x89PNG para un PNG, PK para un zip, { para JSON). «Puedo decodificarlo» y «puedo leerlo» son dos afirmaciones distintas —la misma división que encuentras en un JWT, cuyo segmento de firma es Base64URL sobre bytes en bruto que no son texto en absoluto—. Pega uno en el inspector de JWT y la cabecera y el payload decodifican a JSON mientras la firma queda deliberadamente opaca. (En el navegador, ten en cuenta que atob() devuelve una cadena binaria —un carácter por byte—, no texto UTF-8 decodificado; para convertir bytes UTF-8 en caracteres legibles aún tienes que pasarlos por un TextDecoder.)

Fallo 5: doble codificación y el prefijo data:

Dos rápidos que explican un número sorprendente de decodificaciones atascadas:

  • Doble codificación. Un valor se codifica en Base64 dos veces, así que tu primera decodificación devuelve otra cadena Base64 en vez del contenido —vuelve a decodificar—. La señal es una decodificación que da un ASCII limpio con pinta de Base64 en lugar del contenido que esperabas.
  • El prefijo data: URI. data:image/png;base64,iVBORw0KGgo... no es todo Base64 —solo la parte después de la coma lo es—. Pasa la cadena data:...;base64, entera a un decodificador y falla en el : y el ;. Quita todo hasta la coma incluida antes de decodificar.

La lista para un Base64 que no decodifica

Cuando una cadena no se deja decodificar, no supongas que está truncada. Recorre esto en orden:

  1. Espacios y transporte. Primero quita los saltos de línea y el espacio de copiar y pegar que insertó el transporte. Aparte, si pasó por codificación de formulario, devuelve a + los espacios que lo eran —pero no conviertas todos los espacios sin más (Fallo 3).
  2. Alfabeto. ¿Ves algún - o _? Es Base64URL —tradúcelo a +//, o pon el decodificador en modo URL-safe (Fallo 1).
  3. Padding y longitud. Mira la longitud módulo cuatro: resto 2 → añade ==, resto 3 → añade un solo =. Un resto de 1 significa que la cadena está mal formada o truncada, no solo sin padding —no la rellenes a ciegas (Fallo 2).
  4. Prefijo. Quita un data:...;base64, inicial antes de decodificar (Fallo 5).
  5. Decodifica y luego pregunta qué son los bytes. Si «funcionó» pero se lee como galimatías, el payload era binario o de otro juego de caracteres —no un fallo de decodificación (Fallo 4)—. Si tu primera decodificación es a su vez Base64, decodifica otra vez.

Bajo los cinco está la única idea: Base64 es un reempaquetado reversible de bytes↔texto, nada más. No esconde tus datos, no los encoge y no garantiza que el resultado sea legible —y que una decodificación funcione solo significa que los caracteres volvieron a bytes, no que el contenido esté completo ni sin modificar—. La mayoría de los bugs de «no decodifica» no son corrupción —son un alfabeto equivocado, padding que falta, un + que se volvió espacio, o bytes que nunca fueron texto—. Nombra cuál es, y la cadena que parecía rota se abre limpiamente.