¿Tiene que ocuparse del formato Base64? Entonces esta página es perfecta para Ud. Utilice nuestra práctica herramienta en línea para codificar o decodificar sus datos.

Decodificación Base64 en Perl: una guía completa

Te han entregado una cadena de letras, dígitos y el ocasional + o /, y en el fondo sabes que no es lo que parece. Quizá sea un token montado en una cabecera Authorization, un archivo .b64 desenterrado de un ticket de soporte, un certificado con su armadura de -----BEGIN puesta, o un blob descansando tranquilamente en un archivo de configuración. Abres una terminal, escribes perl, y una pregunta se lleva todo lo demás: ¿cómo recupero los datos de verdad?

La respuesta es corta y tranquilizadora. Perl lleva un módulo de Base64 incluido en el propio lenguaje desde 2002, y una sola llamada a función, decode_base64, hace todo el trabajo: nada que instalar, nada que configurar. Un repaso rápido mientras se prepara el café: Base64 reescribe cada tres bytes de datos como cuatro caracteres de un alfabeto de 64 símbolos, rellenando el final con uno o dos signos = para que el resultado caiga siempre en un múltiplo de cuatro, y por eso la forma codificada suele ser unos 33 por ciento más grande que la original. La página de inicio de este sitio explica el formato al completo, así que esta guía se dedica por completo al lado de Perl: las reglas del decodificador, los dialectos y los formatos del mundo real que de verdad te encontrarás.

La caja de herramientas: cinco funciones, cero instalaciones

Todas las llamadas que necesitas viven en MIME::Base64, que forma parte de la distribución core de Perl desde la 5.8, así que está presente en toda instalación seria, desde la que va embebida en el firmware de un router hasta la de un servidor de base de datos. La comprobación es de una línea:

perl -MMIME::Base64 -e 'print $MIME::Base64::VERSION, "\n"'
# 3.16_01

Aquí está el lado de decodificación del módulo, completo:

Función Qué hace Notas
decode_base64($str) la estrella de este artículo: convierte un blob de Base64 en bytes crudos ignora en silencio cada carácter fuera del alfabeto, para siempre
MIME::Base64::decode($str) el mismo decodificador, llamado sin import la forma que te encontrarás en un montón de scripts más antiguos
decode_base64url($str) decodifica el dialecto seguro para URLs con - y _, con padding o sin él añadida en la 3.11 en 2010; la que lee los JWT
MIME::Base64::decoded_base64_length($str) te dice de qué tamaño será la decodificada, sin decodificar no exportada por defecto, práctica para preajustar buffers
unpack("u", $data) decodifica datos uuencoded, el formato anterior a Base64 integrada en el propio Perl, sin módulo

El mapa de versiones de esas funciones, por si mantienes una flota de máquinas viejas:

Funcionalidad Disponible desde
decode_base64() con el camino rápido en C Perl 5.8 en 2002, cuando el módulo se unió al core
decoded_base64_length() módulo 3.10 en 2010
decode_base64url() módulo 3.11 en 2010
Decodificación silenciosa, sin avisos con entrada sospechosa módulo 3.11 en 2010
La actual línea 3.16 2020, requiere Perl 5.6 o más reciente

Si el Perl de tu sistema no tiene el módulo por alguna razón, y no debería pasar, la reparación es de una de dos líneas: el paquete de la distribución libmime-base64-perl en Debian y Ubuntu, o cpanm MIME::Base64 para descargar la versión actual desde CPAN, donde el módulo vive como paquete de vida dual desde sus días de core. Para la extraña máquina sin compilador de C, el gemelo puro en Perl MIME::Base64::Perl en CPAN ofrece la misma interfaz básica, unas veces más lento pero lo bastante bueno para cualquier cosa menos trabajo a granel. Esa es la historia completa de las dependencias: nada más.

Un decodificador que nunca dice que no

El contrato cabe en una línea. Le pasas una cadena, y te devuelve los bytes decodificados como una cadena Perl corriente que contiene octetos crudos. Sin objetos, sin excepciones, sin flags. La documentación enuncia en una sola frase las dos reglas que definen su personalidad: cualquier carácter que no forme parte del subconjunto Base64 de 65 caracteres se ignora en silencio, y cualquier carácter que aparezca después de un carácter de padding = nunca se decodifica. Esa cortesía es lo más importante de este artículo, así que déjala trabajar por ti una vez:

use MIME::Base64 qw(decode_base64);
print decode_base64("TWFu!"),   "\n";  # Man - el signo de exclamación desaparece sin dejar rastro
print decode_base64("TWFu=XX"), "\n";  # Man - todo lo que hay después de = se omite
print decode_base64("TQ"),      "\n";  # M - sin aviso, sin comentario
print decode_base64("T"),       "\n";  # la cadena vacía, y aún así sin queja

Las dos últimas líneas son la tolerancia en su extremo. TQ lleva un byte completo más cuatro bits sobrantes, y el decodificador simplemente se queda el byte y descarta el resto. T no llega ni a un byte completo, así que el resultado está vacío. No hay modo estricto ni validador en el módulo que restaure la pedantería de vieja escuela: desde la versión 3.11 en 2010, decode_base64 ni siquiera avisa de la entrada truncada, y las versiones anteriores llegaban a emitir un aviso de Fin prematura de los datos base64 bajo -w. Si el blob es incorrecto, de todos modos lo decodifica, y eso te convierte en la puerta de calidad.

Aquí está la política de tolerancia en un solo sitio, para que puedas verla entera de un vistazo:

Entrada Resultado Por qué
"TWFu" Man entrada limpia, el camino feliz
"TWFu!" Man el signo de exclamación no está en el alfabeto, así que se omite
"TWFu=XX" Man nada después del padding se decodifica jamás
"TWFuIFdvcmxkIQ==" Man World! los espacios en blanco donde sean, gratis
"TQ" M cabe un byte completo, los bits sobrantes se descartan en silencio
"T" la cadena vacía ni siquiera un byte completo, y sin aviso tampoco
"ab-cd_efgh" bytes incorrectos en silencio las letras seguras para URLs se descartan como ruido, la trampa clásica

Esa última fila es la que hay que recordar. Un segmento base64url pasado al decodificador estándar no falla: se decodifica a basura con aspecto plausible, porque los caracteres - y _ se tratan como ruido ajeno mientras las letras restantes siguen formando grupos válidos. El decodificador es un testigo, no un portero, así que si la entrada no es de confianza, tú la validas. Basta una pequeña comprobación estricta:

sub strict_base64 {
  my ($blob) = @_;
  $blob =~ s/[\r\n]//g;  # el decodificador ignora estos, y nosotros también
  return 0 unless length($blob) % 4 == 0;
  return $blob =~ /\A[0-9A-Za-z+\/]+(?:={1,2})?\z/ ? 1 : 0;
}
print strict_base64("TWFu"),  "\n";  # 1
print strict_base64("TQ="),   "\n";  # 0 - recuento de padding incorrecto
print strict_base64("ab-cd"), "\n";  # 0 - alfabeto seguro para URLs

Una palabra corta sobre esa regex, aprendida a duras penas: si un sub termina en un return $x =~ /.../ a secas y el resultado del match fallido se pasa directamente a printf, Perl lanza un aviso engañoso de Falta un argumento en printf en lugar de un cero limpio. Fuerza el match con ? 1 : 0 antes de devolver, como hace la función de arriba, y el truco desaparece por completo.

Primero bytes, después caracteres

Recuerda qué devuelve decode_base64: bytes crudos, una cadena corriente sin la flag UTF-8 activada. Qué significan esos bytes es una decisión que solo puedes tomar tú, y es el paso donde el Unicode hace tropezar a la gente. Perl lleva la cuenta de si una cadena contiene caracteres o bytes, y length(), substr() y la mayoría de las expresiones regulares se comportan de forma distinta según la respuesta. La solución es nombrar tu codificación a propósito, con el módulo Encode que viene con cada instalación de Perl:

use MIME::Base64 qw(decode_base64);
use Encode qw(decode);
my $raw  = decode_base64("SMOrbGxvIFdvcmxkIQ==");
my $text = decode("UTF-8", $raw);
print $text, "\n";            # Hëllo World!
print length($text), " chars\n";  # 12
print length($raw),  " bytes\n";  # 13

Ese par de números es toda la lección. El blob es de 13 bytes pero solo de 12 caracteres, porque la letra acentuada ocupa dos bytes en UTF-8. Si saltas el paso del charset, los bytes se imprimirán igual de bien en una terminal UTF-8, y por eso el error se queda oculto hasta que una función de cadenas los cuenta, o los bytes viajan por un pipeline que espera caracteres. Si tienes dudas, decodifica con un charset estricto y deja que la excepción te cuente la verdad sobre los bytes: pasa Encode::FB_CROAK y decode() muere con secuencias inválidas en lugar de la sustitución silenciosa por U+FFFD del valor por defecto, y eso es una ventaja.

La lista corta de charsets que de verdad usarás:

Charset Cuándo usarlo Cuidado con
UTF-8 el supuesto por defecto: APIs, JSON, contenido web, texto moderno las secuencias inválidas se convierten en U+FFFD por defecto; con Encode::FB_CROAK mueren limpias, que es exactamente lo que quieres
Latin-1 texto occidental heredado, un byte por carácter, no puede fallar nunca con gusto desgarra UTF-8 hasta convertirlo en mojibake doblemente codificado
ASCII datos de los que estás seguro de que son texto plano de 7 bits cualquier byte por encima de 127 se convierte en U+FFFD por defecto (muere bajo Encode::FB_CROAK)
UTF-16 texto de Windows, donde la marca de orden de bytes decide la endianidad el BOM es la única pista de endianidad, así que déjalo en los bytes

Y aquí está la trampa de la que te protege el paso del charset. Si los bytes que decodificaste ya son UTF-8 y los pasas por encode("UTF-8", ...) al salir, no obtienes una copia: obtienes una codificación doble, donde cada carácter acentuado se hincha hasta convertirse en dos caracteres propios. El síntoma clásico es un texto que antes leía Hëllo y ahora lee Hëllo, y el receptor al otro lado del cable lo decodificará con fidelidad. Bytes entrando, bytes saliendo, una conversión nombrada en medio.

base64url: el alfabeto para URLs y tokens

La mitad del Base64 que cruza el internet moderno no es el alfabeto estándar en absoluto. El carácter + es la forma en que un navegador codifica un espacio en una query string, y / es un separador de rutas, así que las letras estándar son un desastre en las URLs. El RFC 4648, sección 5, define la solución: un segundo alfabeto que cambia + y / por - y _, y por convención descarta también el padding = y los saltos de línea. El RFC es explícito en que esta codificación no debe considerarse la misma que la codificación base64, y Perl lleva un par dedicado para ella desde la versión 3.11 en 2010:

use MIME::Base64 qw(decode_base64url);
my $raw = decode_base64url("c3Vuc2V0LTQy");
print $raw, "\n";  # sunset-42

Dos cosas que saber. Primera, decode_base64url se lleva bien con la entrada sin padding, que es la forma que de verdad encontrarás en la naturaleza, así que el ritual de restaurar el padding antes que requieren otros lenguajes no se aplica aquí; la entrada con padding también funciona. Segunda, el decodificador estándar es otra bestia: pásale un segmento base64url y obtendrás bytes incorrectos en silencio, porque los caracteres - y _ se descartan como ruido y el resto sigue decodificándose. Usa el decodificador adecuado, o normaliza a mano cuando estés atado a un camino de código heredado:

my $seg = "ab-cd_efgh";
$seg =~ tr{-_}{+/};                    # las letras seguras para URLs, traducidas a casa
$seg .= "=" x (-length($seg) % 4);     # padding restaurado para el decodificador estándar
my $raw = decode_base64($seg);
print unpack("H*", $raw), "\n";  # 69bf9c77f79f82 - siete bytes, de vuelta al alfabeto estándar

Te encontrarás con base64url inmediatamente en los JWT, los tokens que reparte cada API moderna, y en cualquier ID opaco que viva en una URL: IDs de video de once caracteres, UUIDs guardados en el alfabeto seguro para URLs (CPAN tiene Data::UUID::Base64URLSafe justo para esto) y claves de base de datos que necesitan sobrevivir a una barra de direcciones. Y si vas con un Perl viejo anterior a las funciones del core, el módulo independiente MIME::Base64::URLSafe de 2006, un port del codec urlsafe de Python, ofrece urlsafe_b64encode y urlsafe_b64decode; en cualquier cosa desde 3.11 en adelante, las funciones integradas son la mejor opción.

JWTs: leyendo la cabecera y el payload

Un JSON Web Token es, estructuralmente, dos trozos de JSON con un disfraz más un recibo criptográfico. La forma compacta del RFC 7515 son tres segmentos base64url unidos por puntos: la cabecera protegida, el payload y la firma. Dividirlo y leerlo son tres líneas:

use MIME::Base64 qw(decode_base64url);
use JSON::PP;
my $jwt = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJob21lciJ9.uzM6l0c4...";
my ($head_b64, $claims_b64, $sig_b64) = split /\./, $jwt, 3;
my $head   = decode_json(decode_base64url($head_b64));
my $claims = decode_json(decode_base64url($claims_b64));
print $claims->{sub}, " (", $head->{alg}, ")\n";  # homer (HS256)

Fíjate en la división del trabajo: decode_base64url convierte cada segmento en bytes, y decode_json del módulo core JSON::PP, presente desde Perl 5.14, convierte los bytes de la cabecera y del payload en estructuras de datos Perl. El segmento de la firma también es base64url, pero es un resumen criptográfico, así que solo decodificas los dos primeros segmentos y dejas que una librería adecuada se encargue del tercero.

El bache es el que todos olvidan: legible no significa válido. La cabecera y el payload son legibles por diseño, lo cual también significa que cualquiera puede reescribirlas; la firma es la única prueba. Para lo que sea serio, verifica, no te limites a decodificar. El módulo de CPAN Crypt::JWT, que se apoya en CryptX, hace todo el trabajo:

use Crypt::JWT qw(decode_jwt);
my $claims = decode_jwt(
  token        => $jwt,
  key          => $secret,
  accepted_alg => "HS256",
);

Explota con una firma mala, y fijar accepted_alg cierra el agujero de confusión de algoritmos donde un atacante cambia el token a una variante más débil. Un flujo de decodificar e imprimir va bien para inspeccionar un token durante una llamada de soporte; no es autenticación.

Archivos: del .b64 de vuelta al original

Los archivos son donde la cultura de one-liners de Perl realmente brilla, y el trabajo entero cabe en un solo comando. La flag -0777 es el ingrediente secreto, porque slurpea el archivo entero en una sola cadena en lugar de alimentar al decodificador línea por línea:

perl -MMIME::Base64 -0777 -ne 'print decode_base64($_)' < in.b64 > out

La forma línea por línea es segura para una clase específica de archivos: aquellos donde cada línea contiene un múltiplo de cuatro caracteres Base64, que es cierto de todo cuerpo MIME doblado correctamente, ya que 76 es un múltiplo de 4. En el momento en que los puntos de doblado se ponen irregulares - y en los archivos doblados a mano normalmente lo están - la decodificación línea por línea empieza a producir padding en medio de los datos. El modo slurp no tiene tal condición, y por eso es la opción por defecto:

perl -MMIME::Base64 -ne 'print decode_base64($_)' < in.b64 > out

Dentro de un script, el patrón es la coreografía estándar de archivos de Perl, con un detalle callado pero importante: las capas :raw en ambos manejadores, para que Perl nunca intente interpretar los bytes como texto de la plataforma al entrar o al salir:

use MIME::Base64 qw(decode_base64);
use Digest::SHA qw(sha256_hex);
open my $in, "<:raw", $ARGV[0] or die $!;
local $/;
my $blob = <$in>;
close $in;
my $decoded = decode_base64($blob);
print sha256_hex($decoded), "\n";  # compara con la suma de verificación del remitente
open my $out, ">:raw", $ARGV[1] or die $!;
print {$out} $decoded;
close $out;

La línea del hash hace más que presumir. Como el decodificador acepta casi cualquier cosa, una suma de verificación que coincida con lo que publicó el remitente es la única prueba de que el viaje fue exacto al byte. Para archivos verdaderamente gigantescos, el bucle línea por línea es la alternativa de poca memoria, siempre que los puntos de doblado caigan en límites de cuatro caracteres, y MIME::Base64::decoded_base64_length te dice de qué tamaño será la salida antes de comprometerte con un buffer.

Armadura PEM: quita la capa, conserva el DER

Los archivos .pem de cada pila de seguridad son el mismo Base64 con armadura puesta: una línea de cabecera, una línea de pie, y un cuerpo doblado a 64 caracteres según la vieja convención de Privacy Enhanced Mail. El envoltorio es la única parte interesante, porque al decodificador del módulo no le importan las longitudes de línea en absoluto:

use MIME::Base64 qw(decode_base64);
open my $fh, "<:raw", "cert.pem" or die $!;
local $/;
my $blob = <$fh>;
close $fh;
my @body = grep { !/^-----/ && /\S/ } split /\n/, $blob;
my $der = decode_base64(join "", @body);
print length($der), " bytes of DER\n";

Las líneas BEGIN y END se quitan, el resto se une en una sola cadena, y cada salto de línea se ignora por el camino. Para el trabajo cotidiano con certificados las herramientas OpenSSL ya hacen esto por ti; las ocho líneas de arriba son el patrón que hay que recordar cuando necesitas los bytes DER crudos tú mismo, para un hash, una huella o una comparación.

Data URIs: imágenes que llevan su propia dirección

El esquema data: del RFC 2397 inyecta un payload directamente en una URL: data:, un tipo de media opcional, una flag opcional ;base64, una coma, y los datos. Los medios binarios como las imágenes usan la flag, así que el payload es el alfabeto estándar con padding, y el decodificador corriente se encarga de él tras un pequeño corte:

use MIME::Base64 qw(decode_base64);
my $uri = "data:image/png;base64,iVBORw0KGgo...";
$uri =~ s/^data:[^,]+,// or die "not a data URI";
my $raw = decode_base64($uri);
print unpack("H8", $raw), "\n";  # 89504e47: los bytes mágicos de PNG

Comprobar los bytes mágicos es la jugada. Si esos primeros ocho caracteres hexadecimales no son 89504e47, la imagen no es un PNG pase lo que pase el tipo de media, y un decodificador que nunca se queja hace posible exactamente ese tipo de mentira callada.

Primos y fósiles: uuencode y los demás alfabetos

Antes de que Base64 ganara, la forma clásica de UNIX para enviar un binario por correo era uuencode, y todavía te la encontrarás en listas de correo viejas y herramientas viejas. La buena noticia: Perl tiene un decodificador integrado para ella, sin módulo, gracias a la plantilla u de pack y unpack:

my $uu   = pack("u", "Hello, World!");
print $uu, "\n";  # -2&5L;&\L(%=O<FQD(0`` más un salto de línea
my $back = unpack("u", $uu);
print $back, "\n";  # Hello, World!

Las dos llamadas son inversas exactas, y esa es toda la historia que necesitas; y el clásico comando uuencode de la cadena de herramientas de UNIX simplemente envuelve las líneas desnudas en una cabecera begin y un pie end, así que el payload que estás decodificando es la parte que hay entre ellas.

Base64 también tiene primos de dialecto, y saber qué decodificador se come cuál te ahorra una sesión de depuración:

Dialecto Doblado Dónde te lo encuentras Qué hace decode_base64
MIME (RFC 2045) 76 caracteres cuerpos de correo lo decodifica tal cual: los saltos de línea y el CRLF se ignoran
PEM (RFC 1421) 64 caracteres certificados y claves lo decodifica tal cual
PKIX (RFC 7468) 64 caracteres estructuras textuales X.509 lo decodifica tal cual
Armadura OpenPGP (RFC 9580) 76 caracteres más una línea CRC24 claves y firmas PGP lo decodifica tal cual, la línea de suma de verificación se ignora
IMAP (RFC 3501) ninguno nombres de buzones no es este alfabeto: la barra se convierte en coma, traduce las letras primero

La moraleja: para toda variante del alfabeto estándar que solo se dobla de forma distinta, un decodificador tolerante lo cubre todo. Solo cuando cambia el propio alfabeto necesitas traducir los caracteres primero.

Configuración, bases de datos y variables de entorno

Las plataformas de contenedores, las consolas en la nube y un número sorprendente de archivos de configuración guardan credenciales y documentos pequeños como cadenas Base64 opacas, porque un blob de letras y dígitos parece menos peligroso que la contraseña que es. La decodificación siempre es la misma, de dos fases: decode_base64 más una decisión de charset:

use MIME::Base64 qw(decode_base64);
use Encode qw(decode);
my $secret = decode("UTF-8", decode_base64($config->{api_key}));

La razón de que el formato sea tan popular en este sitio es exactamente la que el RFC 4648 advierte: los humanos dejan de notar que los datos son legibles. Así que trata la salida decodificada como confidencial desde el momento en que se devuelve, y mantén tanto el blob como su resultado fuera de los archivos de registro, las alertas y los volcados de depuración.

La misma forma aparece en las bases de datos, donde los datos binarios a menudo viajan en una columna TEXT como Base64 porque la columna no puede prometer que dejará pasar bytes arbitrarios intactos:

use MIME::Base64 qw(decode_base64);
my $icon = decode_base64($row->{icon_data});
open my $fh, ">:raw", "icon.png" or die $!;
print {$fh} $icon;
close $fh;

Correo: partes MIME y adjuntos

El correo es donde Base64 consiguió su nombre, y la tolerancia del módulo está diseñada para exactamente este tráfico. Una parte MIME con Content-Transfer-Encoding: base64 llega como líneas de texto de 76 caracteres terminadas en CRLF, y el decodificador se come el sobre entero tal cual, saltos de línea incluidos:

use MIME::Base64 qw(decode_base64);
my $part_body = "SGVsbG8sIHF1ZXJ5IQpUaGlzIE1JTUUgcGFydCB0cmF2ZWxsZWQgYXMgYmFzZTY0LCB3cmFwcGVk";
$part_body .= "\r\nIGF0IDc2IGNoYXJhY3RlcnMsIENSTEYgYmV0d2VlbiBsaW5lcy4=";
my $text = decode_base64($part_body);
print $text;  # el cuerpo original de dos líneas del mensaje

Si construyes o analizas correo con un framework, no haces nada de esto a mano: MIME::Lite codifica en base64 un adjunto por ti cuando pasas Encoding => "base64" a attach, y Email::MIME hace lo mismo automáticamente. La versión hecha a mano de arriba es para el correo que llega como texto crudo en un registro, un ticket o un mensaje reenviado, que en la práctica es un montón.

Trampas, recogidas y clasificadas

El módulo es lo bastante pequeño para memorizarlo, así que aquí está la lista completa de trampas en un solo sitio, ordenada aproximadamente por lo a menudo que pica:

Trampa Qué pasa Solución
Alimentar al decodificador estándar con un segmento base64url los - y _ se descartan como ruido, y el resto se decodifica en bytes incorrectos en silencio usa decode_base64url, o traduce el alfabeto y restaura el padding primero
Confiar en el silencio con entrada corrupta caracteres ajenos, truncamiento y un alfabeto equivocado se decodifican todos sin un solo aviso haz primero la comprobación estricta, y verifica con un hash cuando el original esté disponible
Caracteres no ASCII dentro del blob una letra acentuada perdida o un espacio Unicode pegado se ignora en silencio, encogiendo el resultado sin comentario la misma comprobación estricta rechaza todo lo que esté fuera del alfabeto de 7 bits
Tratar el resultado como texto los bytes no llevan flag UTF-8, así que length() cuenta bytes y las funciones de cadenas se hacen una idea equivocada encadena decode("UTF-8", $raw) o el charset que elijas antes de cualquier procesamiento de texto
Codificación doble al salir pasar bytes ya UTF-8 por encode("UTF-8", ...) convierte Hëllo en Hëllo codifica caracteres, nunca bytes crudos, y comprueba la flag con utf8::is_utf8() si tienes dudas
Decodificar un archivo línea por línea con doblados irregulares las líneas que no terminan en límites de cuatro caracteres producen padding en medio de la salida slurpea con -0777, o garantiza puntos de doblado de cuatro caracteres
Devolver un match fallido de regex a un contexto numérico un sub que termina en return $x =~ /.../ y lo pasa a printf lanza un aviso engañoso de Falta un argumento en printf fuerza el match: return $x =~ /.../ ? 1 : 0
Código viejo que espera el aviso viejo los scripts anteriores a 3.11 que dependían del aviso Fin prematura de los datos base64 bajo -w ahora no ven nada añade tu propia comprobación estricta; el aviso se fue para siempre
Suponer que decodificar es verificar el decodificador acepta casi cualquier cosa y no dice nada al respecto una suma de verificación coincidente o una firma verificada es la única prueba que importa
Registrar en el log lo que decodificas el formato no oculta nada, y el archivo de registro es justo donde lo encuentra el siguiente mantén los secretos decodificados fuera de los registros, las alertas y los volcados de depuración

Buenos hábitos

Los hábitos que evitan que Base64 se salga con la suya con tus scripts:

  • Espera bytes, siempre. Escribe código que sepa que decode_base64 devuelve octetos crudos, y encadena el decode de charset explícitamente en lugar de esperar que la terminal haga lo correcto.
  • Nombra tu charset. Por defecto, UTF-8, y cambia solo cuando los datos digan lo contrario. El fallo estricto de decode("UTF-8", ..., Encode::FB_CROAK) es una ventaja: te dice que los bytes no son lo que supusiste.
  • Empareja el alfabeto con la fuente. decode_base64url para URLs, tokens e IDs; decode_base64 para todo lo demás. Los dos alfabetos no son intercambiables, y el decodificador no te dirá cuando elijas mal.
  • Valida antes de decodificar. No hay flag de modo estricto en este módulo, así que una pequeña comprobación es el portero de entrada.
  • Slurpea los archivos por defecto. -0777 o local $/ = undef elimina una clase entera de bugs de puntos de doblado, y el coste de memoria no es problema para los archivos que de verdad decodificas.
  • Usa :raw en cada manejador de archivo. Binario entrando, binario saliendo. Las capas de texto son para humanos, no para bytes.
  • Verifica con un hash. Cuando el original esté disponible, una suma de verificación coincidente es la única prueba de una decodificación exacta al byte.
  • Nunca registres en el log lo que decodificas. El formato no oculta nada.

Una historia corta, contada por el changelog

El formato es viejo, y la relación de Perl con él es más vieja de lo que parece. Algunas fechas verificadas, en orden:

  • El código C es anterior a Perl 5. El decodificador rápido dentro del módulo desciende de código de metamail, el programa de correo de Bellcore, con copyright en 1991, tres años antes del primer lanzamiento de Perl 5. Cuando hoy llamas a decode_base64, un pedazo de los noventa está haciendo el trabajo.
  • Nacido en las herramientas web. El módulo empezó como LWP::Base64 dentro de libwww-perl a mediados de los noventa, escrito por Martijn Koster y Joerg Reichelt, y ascendió a su propia distribución de CPAN, MIME::Base64, en abril de 1997, versión 2.00, con la entrada del changelog que dice basado en libwww-perl-5.08.
  • La era de los avisos. Desde la 2.03 en 1997, la entrada truncada producía un aviso de Fin prematura de los datos base64 bajo -w en lugar de un croak, y la 2.11 en 1999 arregló las compilaciones que avisaban de datos que estaban bien. Fue una década más nerviosa para los decodificadores.
  • En el core desde 2002. Perl 5.8 metió el módulo en la distribución core, y la sincronización 2.13 con el core ese mismo diciembre trajo el soporte EBCDIC por el camino, y por eso el codificador y el decodificador siguen funcionando en las mainframes.
  • El dialecto seguro para URLs llegó al core de Perl en 2010. La versión 3.11 añadió decode_base64url y su hermano, cuatro años después de que el módulo independiente MIME::Base64::URLSafe aterrizara en CPAN en 2006, el mismo año en que el RFC 4648 codificó el dialecto.
  • El silenciamiento. Ese mismo lanzamiento 3.11 quitó hasta el viejo aviso de truncamiento, por si acaso la entrada sospechosa era intencionada - y cada lanzamiento desde entonces, incluida la actual línea 3.16 de 2020, ha mantenido al decodificador educado y callado.

Datos curiosos, específicamente Perl

Para redondear el recorrido, las curiosidades que hacen de esta historia una buena:

  • Decodifica el propio nombre del formato. decode_base64("YmFzZTY0") devuelve base64. Ha sido cierto desde 1997 y lo será para siempre.
  • El decodificador es un fantasma educado. En su historia 3.x nunca ha lanzado una excepción con entrada mala. Corrupta, truncada, alfabeto equivocado: lo decodifica todo y no se queja de nada, un comportamiento que el changelog cementó a propósito en 2010.
  • El doblado MIME es un múltiplo de cuatro a propósito. El límite de 76 caracteres corresponde a diecinueve grupos de tres bytes, 57 bytes en total, por cuatro caracteres, y por eso la decodificación línea por línea es segura con cualquier cuerpo MIME doblado correctamente e insegura con todo lo demás.
  • uuencode nunca imprime una letra minúscula. Su alfabeto se detiene en el guion bajo, y por eso los archivos uuencoded viejos parecen escritos por una máquina de solo mayúsculas, y por eso Perl sigue llevando un decodificador integrado para un formato más viejo que el internet.
  • Perl llegó a traer su propio comando decode-base64. Las versiones desde la 2.14 en 2003 hasta la 3.05 en 2004 incluían encode-base64, decode-base64 y sus gemelos quoted-printable como scripts; la 3.06 en 2005 los movió a la distribución aparte MIME-Base64-Scripts. Si encuentras una instalación vieja con ese comando en el PATH, ya sabes de dónde viene.
  • Los IDs de video de YouTube son base64url con disfraz. El ID de once caracteres en tu barra de direcciones es un número de 64 bits en el alfabeto seguro para URLs con el padding quitado, así que cada video que hayas visto tiene una cadena Base64 en su URL, y decode_base64url puede leerla.
  • La tolerancia es un estándar, no un bug. La regla MIME de ser liberal en lo que aceptas es la razón de que este decodificador sobreviva a tres décadas de datos desordenados, y la razón de que el RFC 4648 advierta que esa misma tolerancia se puede convertir en un canal encubierto si confías en entrada no confiable.

Así que la próxima vez que una cadena de letras, dígitos, más y barra aterrice en tu terminal, ya conoces la historia entera. Una llamada a función hace el trabajo, el decodificador es un fantasma educado que nunca te negará nada, base64url tiene su propio decodificador, el charset es una decisión que tomas a propósito, los archivos entran crudos y salen crudos, y un hash es la única prueba que importa. Y si un día necesitas hacer el viaje en la otra dirección, envolviendo tus propios datos crudos en un sobre de texto y enviándolos al mundo, el artículo relacionado sobre la codificación Base64 en Perl, enlazado abajo, cubre ese ritual con la misma profundidad.

Última actualización: 2026-09-08

Artículo relacionado: Codificación Base64 en Perl: una guía completa