Vous avez à traiter le format Base64 ? Alors ce site est parfait pour vous ! Utilisez notre outil en ligne super pratique pour encoder ou décoder vos données.

Décodage Base64 en Perl : un guide complet

On vous a confié une chaîne de lettres, de chiffres, et, de temps en temps, un + ou un /, et vous savez au fond de vous que ce n'est pas ce que ça semble être. Peut-être est-ce un token qui voyage dans un en-tête Authorization, un fichier .b64 exhumé d'un ticket de support, un certificat revêtu de son armure -----BEGIN, ou un blob bien tranquille au fond d'un fichier de configuration. Vous ouvrez un terminal, vous tapez perl, et une seule question s'empare de tout le reste : comment récupérer les vraies données ?

La réponse est courte et rassurante. Perl livre un module Base64 avec le langage lui-même depuis 2002, et un seul appel de fonction, decode_base64, fait tout le travail : rien à installer, rien à configurer. Un petit rappel pendant que le café infuse : le Base64 réécrit tous les trois octets de données en quatre caractères tirés d'un alphabet de 64 symboles, en complétant la fin avec un ou deux signes = pour que le résultat tombe toujours sur un multiple de quatre, ce qui explique que la forme encodée soit typiquement environ 33 pour cent plus longue que l'origine. La page d'accueil de ce site détaille le format en profondeur, donc ce guide passe tout son temps du côté Perl : les règles du décodeur, les dialectes, et les formats du monde réel que vous croiserez vraiment.

La boîte à outils : cinq fonctions, zéro installation

Tous les appels dont vous avez besoin vivent dans MIME::Base64, qui fait partie de la distribution core de Perl depuis la 5.8, si bien que le module est présent sur toute installation sérieuse, de celle embarquée dans le firmware d'un routeur à celle d'un serveur de base de données. La vérification tient en une ligne :

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

Voici le côté décodage du module, dans sa totalité :

Fonction Ce qu'elle fait Notes
decode_base64($str) l'étoile de cet article : transforme un blob Base64 en octets bruts ignore silencieusement chaque caractère hors alphabet, pour toujours
MIME::Base64::decode($str) le même décodeur, appelé sans import la forme que vous croiserez dans pas mal de scripts plus anciens
decode_base64url($str) décode le dialecte sûr pour les URL avec - et _, avec ou sans padding ajoutée en 3.11 en 2010 ; c'est elle qui lit les JWT
MIME::Base64::decoded_base64_length($str) annonce la taille des données une fois décodées, sans décoder non exportée par défaut, pratique pour prédimensionner les buffers
unpack("u", $data) décode les données uuencodées, le format d'avant le Base64 intégrée à Perl lui-même, aucun module requis

La carte des versions de ces fonctions, au cas où vous entretieneriez une flotte de vieilles machines :

Fonctionnalité Disponible depuis
decode_base64() avec le chemin rapide en C Perl 5.8 en 2002, quand le module a rejoint le core
decoded_base64_length() module 3.10 en 2010
decode_base64url() module 3.11 en 2010
Décodage silencieux, aucun avertissement sur les entrées suspectes module 3.11 en 2010
La ligne 3.16 actuelle 2020, exige Perl 5.6 ou plus récent

Si le Perl de votre système n'a pas le module pour une raison quelconque, et il ne devrait pas, la correction tient en une de ces deux lignes : le paquet de distribution libmime-base64-perl sur Debian et Ubuntu, ou cpanm MIME::Base64 pour tirer la version actuelle de CPAN, où le module vit en paquet dual-life depuis ses jours de core. Pour la machine curieuse sans compilateur C, la jumelle purement Perl MIME::Base64::Perl sur CPAN fournit la même interface de base, quelques fois plus lente mais largement suffisante pour tout sauf le travail de masse. C'est toute l'histoire des dépendances : rien d'autre.

Un décodeur qui ne dit jamais non

Le contrat tient en une ligne. Donnez-lui une chaîne, et il vous rend les octets décodés en guise de chaîne Perl ordinaire contenant des octets bruts. Pas d'objets, pas d'exceptions, pas de drapeaux. La documentation résume en une seule phrase les deux règles qui définissent son caractère : tout caractère qui ne fait pas partie du sous-ensemble Base64 de 65 caractères est ignoré silencieusement, et tout caractère apparaissant après un caractère de padding = n'est jamais décodé. Cette politesse est le point le plus important de cet article, alors laissez-la travailler pour vous une fois :

use MIME::Base64 qw(decode_base64);
print decode_base64("TWFu!"),   "\n";  # Man - le point d'exclamation s'évapore sans laisser de trace
print decode_base64("TWFu=XX"), "\n";  # Man - tout ce qui suit le = est sauté
print decode_base64("TQ"),      "\n";  # M - pas d'avertissement, pas de commentaire
print decode_base64("T"),       "\n";  # la chaîne vide, et toujours pas de plainte

Les deux dernières lignes sont l'indulgence à son extrême. TQ porte un octet complet plus quatre bits en trop, et le décodeur garde simplement l'octet et jette le reste. T ne porte même pas un octet complet, d'où un résultat vide. Il n'y a ni mode strict ni validateur dans le module pour réinstaurer la maniaquerie old school : depuis la version 3.11 en 2010, decode_base64 ne prévient même plus sur une entrée tronquée, et les versions plus anciennes marmonnaient un avertissement Premature end of base64 data sous -w. Si le blob est faussé, il décode quand même, ce qui fait de vous le contrôleur qualité.

Voici la politique d'indulgence réunie en un seul endroit, pour la voir toute entière d'un coup d'œil :

Entrée Résultat Pourquoi
"TWFu" Man entrée propre, le chemin sans accroc
"TWFu!" Man le point d'exclamation n'est pas dans l'alphabet, donc il est sauté
"TWFu=XX" Man rien n'est décodé après le padding
"TWFuIFdvcmxkIQ==" Man World! les espaces sont bienvenus n'importe où
"TQ" M un octet complet passe, les bits en trop sont jetés discrètement
"T" la chaîne vide pas même un octet complet, et pas d'avertissement non plus
"ab-cd_efgh" des octets faussés, silencieusement les lettres sûres pour les URL sont jetées comme du bruit, le piège classique

Cette dernière ligne est celle à retenir. Un segment base64url remis au décodeur standard ne plante pas : il se décode en une ordure qui a l'air plausible, parce que les caractères - et _ sont traités comme du bruit étranger tandis que les lettres restantes forment encore des groupes valides. Le décodeur est un témoin, pas un gardien, donc si l'entrée n'est pas de confiance, c'est vous qui la validez. Une petite vérification stricte suffit :

sub strict_base64 {
  my ($blob) = @_;
  $blob =~ s/[\r\n]//g;  # le décodeur ignore ces caractères, et nous aussi
  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 - mauvais nombre de padding
print strict_base64("ab-cd"), "\n";  # 0 - alphabet sûr pour les URL

Un petit mot sur cette expression régulière, tiré d'une leçon payée cash : si une sub se termine par un return $x =~ /.../ tout nu et que le résultat d'un échec de correspondance est passé tel quel dans printf, Perl lève un avertissement Missing argument in printf trompeur au lieu d'un zéro propre. Forcez la correspondance avec ? 1 : 0 avant de retourner, comme le fait la fonction ci-dessus, et le tour disparaît complètement.

Les octets d'abord, les caractères ensuite

Souvenez-vous de ce que decode_base64 renvoie : des octets bruts, une chaîne ordinaire sans drapeau UTF-8. Ce que ces octets veulent dire est une décision que seul vous pouvez prendre, et c'est l'étape où l'Unicode fait trébucher tout le monde. Perl suit si une chaîne contient des caractères ou des octets, et length(), substr(), et la plupart des expressions régulières se comportent différemment selon la réponse. La solution, c'est de nommer votre encodage exprès, avec le module Encode livré avec chaque installation 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

Cette paire de nombres est toute la leçon. Le blob fait 13 octets mais seulement 12 caractères, parce que la lettre accentuée prend deux octets en UTF-8. Sautez l'étape du charset et les octets s'afficheront quand même sans problème dans un terminal UTF-8, ce qui est exactement la raison pour laquelle l'erreur reste cachée jusqu'à ce qu'une fonction de chaîne les compte, ou que les octets traversent une chaîne de traitement qui attend des caractères. En cas de doute, décodez avec un charset strict et laissez l'exception vous dire la vérité sur les octets : passez Encode::FB_CROAK et decode() meurt sur les séquences invalides au lieu du remplacement discret par U+FFFD de la valeur par défaut, ce qui est un vrai avantage.

La courte liste des charsets que vous utiliserez vraiment :

Charset Quand l'utiliser À surveiller
UTF-8 l'hypothèse par défaut : API, JSON, contenu web, texte moderne les séquences invalides deviennent U+FFFD par défaut ; avec Encode::FB_CROAK elles meurent proprement, ce qui est exactement ce qu'on veut
Latin-1 texte occidental legacy, un octet par caractère, ne peut jamais échouer elle transformera joyeusement du UTF-8 en charabia doublement encodé
ASCII des données que vous êtes sûr d'être du texte 7 bits pur tout octet au-dessus de 127 devient U+FFFD par défaut (meurt sous Encode::FB_CROAK)
UTF-16 texte Windows, où la marque d'ordre des octets décide de l'endianness le BOM est le seul indice d'endianness, donc gardez-le dans les octets

Et voici le piège contre lequel l'étape du charset vous protège. Si les octets que vous avez décodés sont déjà du UTF-8 et que vous les passez dans encode("UTF-8", ...) à la sortie, vous n'obtenez pas une copie : vous obtenez un double encodage, où chaque caractère accentué gonfle en deux caractères de son propre chef. Le symptôme classique, c'est un texte qui se lisait Hëllo et qui se lit désormais Hëllo, et le récepteur de l'autre bout du câble le décodera fidèlement. Des octets en entrée, des octets en sortie, une seule conversion nommée entre les deux.

base64url : l'alphabet des URL et des tokens

La moitié du Base64 qui traverse l'internet moderne n'est pas du tout l'alphabet standard. Le caractère + est la façon dont un navigateur encode une espace dans une chaîne de requête, et / est un séparateur de chemin, donc les lettres standard sont un désastre dans les URL. La RFC 4648, section 5, définit la solution : un deuxième alphabet qui échange + et / contre - et _, et qui, par convention, jette aussi le padding = et les retours à la ligne. La RFC est explicite : cet encodage ne doit pas être considéré comme identique à l'encodage base64, et Perl a un duo dédié pour ça depuis la version 3.11 en 2010 :

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

Deux choses à savoir. D'abord, decode_base64url est contente d'une entrée sans padding, ce qui est la forme que vous trouverez vraiment dans la nature, donc le rituel de restaurer d'abord le padding que d'autres langues exigent ne s'applique pas ici ; une entrée avec padding fonctionne aussi. Ensuite, le décodeur standard est une autre bête : nourrissez-le d'un segment base64url et vous obtenez des octets faussés, silencieusement, parce que les caractères - et _ sont jetés comme du bruit et que le reste se décode quand même. Utilisez le bon décodeur, ou normalisez à la main quand vous êtes coincé sur un chemin de code legacy :

my $seg = "ab-cd_efgh";
$seg =~ tr{-_}{+/};                    # les lettres sûres pour les URL, traduites au pays
$seg .= "=" x (-length($seg) % 4);     # le padding restauré pour le décodeur standard
my $raw = decode_base64($seg);
print unpack("H*", $raw), "\n";  # 69bf9c77f79f82 - sept octets, revenus dans l'alphabet standard

Vous croiserez base64url immédiatement dans les JWT, les tokens que chaque API moderne remet, et dans n'importe quel ID opaque qui vit dans une URL : des ID de vidéo de onze caractères, des UUID stockés dans l'alphabet sûr pour les URL (CPAN a Data::UUID::Base64URLSafe exactement pour ça), et des clés de base de données qui doivent survivre à une barre d'adresse. Et si vous êtes sur un vieux Perl antérieur aux fonctions du core, le module autonome MIME::Base64::URLSafe de 2006, un port du codec urlsafe de Python, fournit urlsafe_b64encode et urlsafe_b64decode ; sur tout ce qui est 3.11 ou plus, les fonctions intégrées sont le bon choix.

Les JWT : lire l'en-tête et le payload

Un JSON Web Token est, structurellement, deux morceaux de JSON déguisés plus un reçu cryptographique. La forme compacte de la RFC 7515, ce sont trois segments base64url reliés par des points : l'en-tête protégé, le payload, et la signature. Découper et lire un token tient en trois lignes :

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)

Notez la division du travail : decode_base64url transforme chaque segment en octets, et decode_json du module core JSON::PP, présent depuis Perl 5.14, transforme les octets de l'en-tête et du payload en structures de données Perl. Le segment de signature est aussi du base64url, mais c'est un digest cryptographique, donc vous ne décodez que les deux premiers segments et laissez une vraie bibliothèque s'occuper du troisième.

Le piège, c'est celui que tout le monde oublie : lisible ne veut pas dire valide. L'en-tête et le payload sont lisibles par conception, ce qui veut aussi dire que n'importe qui peut les réécrire ; la signature est la seule preuve. Pour tout ce qui est réel, vérifiez, ne vous contentez pas de décoder. Le module CPAN Crypt::JWT, qui se construit sur CryptX, fait tout le travail :

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

Il croak sur une mauvaise signature, et fixer accepted_alg referme le trou de confusion d'algorithmes où un attaquant bascule le token vers une variante plus faible. Un flux décodage-affichage est parfait pour inspecter un token pendant un appel de support ; ce n'est pas une authentification.

Les fichiers : du .b64 jusqu'à l'original

Les fichiers, c'est là que la culture des one-liners de Perl brille vraiment, et tout le travail tient dans une seule commande. Le drapeau -0777 est l'ingrédient secret, parce qu'il avale le fichier entier dans une seule chaîne au lieu de nourrir le décodeur ligne par ligne :

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

La forme ligne par ligne est sûre pour une seule classe précise de fichiers : ceux où chaque ligne contient un multiple de quatre caractères Base64, ce qui est vrai de tout corps correctement enveloppé en MIME, puisque 76 est un multiple de 4. Le moment où les points d'enveloppe deviennent irréguliers - et dans les fichiers enveloppés à la main, c'est souvent le cas - le décodage ligne par ligne commence à produire du padding au milieu des données. Le mode avaleur n'a pas cette condition, ce qui en fait le choix par défaut :

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

Dans un script, le motif est la danse fichier standard de Perl, avec un détail discret mais important : les calques :raw sur les deux poignées, pour que Perl n'essaie jamais d'interpréter les octets comme du texte de la plateforme en entrant ou en sortant :

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";  # comparez avec la somme de contrôle de l'expéditeur
open my $out, ">:raw", $ARGV[1] or die $!;
print {$out} $decoded;
close $out;

La ligne de hash fait plus que faire parade. Parce que le décodeur acceptera presque n'importe quoi, une somme de contrôle qui correspond à ce que l'expéditeur a publiée est la seule preuve que le trajet a été exact à l'octet près. Pour les fichiers vraiment géants, la boucle ligne par ligne est l'alternative économe en mémoire, à condition que les enveloppes tombent sur des frontières de quatre caractères, et MIME::Base64::decoded_base64_length vous dit à l'avance quelle sera la taille de la sortie avant que vous ne vous engagiez sur un buffer.

L'armure PEM : retirer le manteau, garder le DER

Les fichiers .pem de toute pile de sécurité, c'est le même Base64 revêtu d'une armure : une ligne d'en-tête, une ligne de pied, et un corps enveloppé à 64 caractères selon l'ancienne convention Privacy Enhanced Mail. L'enveloppe est la seule partie intéressante, parce que le décodeur du module ne se soucie absolument pas des longueurs de ligne :

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";

Les lignes BEGIN et END sont retirées, le reste est joint en une seule chaîne, et chaque retour à la ligne est ignoré en chemin. Pour le travail courant sur les certificats, l'outillage OpenSSL le fait déjà pour vous ; les huit lignes ci-dessus sont le motif à retenir quand vous avez besoin vous-même des octets DER bruts, pour un hash, une empreinte, ou une comparaison.

Les data URIs : des images qui portent leur propre adresse

Le schéma data: de la RFC 2397 place un payload directement dans une URL : data:, un type média optionnel, un drapeau ;base64 optionnel, une virgule, et les données. Les médias binaires comme les images utilisent le drapeau, donc le payload est l'alphabet standard avec padding, et le décodeur ordinaire le gère après une petite découpe :

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 : les octets magiques du PNG

Vérifier les octets magiques, c'est le coup à jouer. Si ces huit premiers caractères hexadécimaux ne sont pas 89504e47, l'image n'est pas un PNG quoi que le type média prétende, et un décodeur qui ne se plaint jamais rend précisément ce genre de mensonge discret possible.

Cousins et fossiles : uuencode et les autres alphabets

Avant que le Base64 ne gagne, la façon classique de UNIX d'envoyer un binaire par courrier était uuencode, et vous le croiserez encore dans de vieilles listes de diffusion et de vieux outils. La bonne nouvelle : Perl a un décodeur intégré pour ça, aucun module requis, grâce au modèle u dans pack et unpack :

my $uu   = pack("u", "Hello, World!");
print $uu, "\n";  # -2&5L;&\L(%=O<FQD(0`` plus un retour à la ligne
my $back = unpack("u", $uu);
print $back, "\n";  # Hello, World!

Les deux appels sont des inverses exacts, c'est toute l'histoire dont vous avez besoin, et la commande classique uuencode de la boîte à outils UNIX enveloppe simplement les lignes nues dans un en-tête begin et un pied end, donc le payload que vous décodez est la partie entre les deux.

Le Base64 a aussi des cousins de dialecte, et savoir quel décodeur mange quoi vous épargne une session de débogage :

Dialecte Enveloppe Où vous le croisez Ce que fait decode_base64
MIME (RFC 2045) 76 caractères corps de courrier le décode tel quel : retours à la ligne et CRLF sont ignorés
PEM (RFC 1421) 64 caractères certificats et clés le décode tel quel
PKIX (RFC 7468) 64 caractères structures textuelles X.509 le décode tel quel
armure OpenPGP (RFC 9580) 76 caractères plus une ligne CRC24 clés et signatures PGP le décode tel quel, la ligne de somme de contrôle est simplement ignorée
IMAP (RFC 3501) aucun noms de boîtes aux lettres pas cet alphabet : le slash devient une virgule, traduisez d'abord les lettres

À retenir : pour chaque variante de l'alphabet standard qui ne fait qu'envelopper différemment, un seul décodeur indulgent les couvre toutes. C'est seulement quand l'alphabet lui-même change qu'il faut traduire les caractères d'abord.

Configuration, bases de données et variables d'environnement

Les plateformes de conteneurs, les consoles cloud, et un nombre surprenant de fichiers de configuration stockent des identifiants et de petits documents sous forme de chaînes Base64 opaques, parce qu'un blob de lettres et de chiffres a l'air moins dangereux que le mot de passe qu'il est. Le décodage, c'est toujours les mêmes deux étapes : decode_base64 plus une décision de charset :

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

La raison pour laquelle le format est si populaire à cet endroit est exactement celle dont la RFC 4648 met en garde : les humains finissent par ne plus remarquer que les données sont lisibles. Donc traitez la sortie décodée comme confidentielle dès le moment où elle est rendue, et gardez à la fois le blob et son résultat hors des fichiers de log, des alertes, et des dévers de débogage.

La même forme apparaît dans les bases de données, où les données binaires voyagent souvent dans une colonne TEXT en Base64 parce que la colonne ne peut pas promettre de laisser passer des octets arbitraires intacts :

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;

Le courrier : parties MIME et pièces jointes

Le courrier électronique, c'est là que le Base64 a pris son nom, et l'indulgence du module est conçue exactement pour ce trafic. Une partie MIME avec Content-Transfer-Encoding: base64 arrive en lignes de 76 caractères de texte terminé par CRLF, et le décodeur avale l'enveloppe entière telle quelle, retours à la ligne compris :

use MIME::Base64 qw(decode_base64);
my $part_body = "SGVsbG8sIHF1ZXJ5IQpUaGlzIE1JTUUgcGFydCB0cmF2ZWxsZWQgYXMgYmFzZTY0LCB3cmFwcGVk";
$part_body .= "\r\nIGF0IDc2IGNoYXJhY3RlcnMsIENSTEYgYmV0d2VlbiBsaW5lcy4=";
my $text = decode_base64($part_body);
print $text;  # le corps du message original, sur deux lignes

Si vous construisez ou parsez du courrier avec un framework, vous ne faites rien de tout ça à la main : MIME::Lite encode en base64 une pièce jointe pour vous quand vous passez Encoding => "base64" à attach, et Email::MIME fait de même automatiquement. La version faite main ci-dessus est pour le courrier qui arrive en texte brut dans un log, un ticket, ou un message transféré, ce qui, en pratique, en fait une bonne partie.

Les pièges, collectés et classés

Le module est assez petit pour être mémorisé, donc voici toute la liste des pièges en un seul endroit, classée à peu près par fréquence de morsure :

Piège Ce qui se passe Correction
Nourrir le décodeur standard d'un segment base64url les - et _ sont jetés comme du bruit, et le reste se décode en octets faussés, silencieusement utilisez decode_base64url, ou traduisez l'alphabet et restaurez le padding d'abord
Se fier au silence sur une entrée corrompue caractères étrangers, troncature, et mauvais alphabet se décodent tous sans le moindre avertissement passez la vérification stricte d'abord, et vérifiez avec un hash quand l'original est disponible
Caractères non ASCII dans le blob une lettre accentuée égarée ou une espace Unicode collée est ignorée silencieusement, rétrécissant le résultat sans un mot la même vérification stricte rejette tout ce qui est hors de l'alphabet 7 bits
Traiter le résultat comme du texte les octets ne portent pas de drapeau UTF-8, donc length() compte des octets et les fonctions de chaîne ont le mauvais tableau enchaînez decode("UTF-8", $raw) ou votre charset choisi avant tout traitement de texte
Double encodage à la sortie faire passer des octets déjà UTF-8 dans encode("UTF-8", ...) transforme Hëllo en Hëllo encodez des caractères, jamais des octets bruts, et vérifiez le drapeau avec utf8::is_utf8() en cas de doute
Décoder un fichier ligne par ligne avec des enveloppes irrégulières les lignes qui ne finissent pas sur des frontières de quatre caractères produisent du padding au milieu de la sortie avalez avec -0777, ou gardez des points d'enveloppe de quatre caractères
Retourner une correspondance d'expression régulière échouée dans un contexte numérique une sub qui se termine par return $x =~ /.../ et la passe à printf lève un avertissement Missing argument in printf trompeur forcez la correspondance : return $x =~ /.../ ? 1 : 0
Vieux code qui attend l'ancien avertissement les scripts d'avant 3.11 qui comptaient sur le marmonnement Premature end of base64 data sous -w ne voient plus rien ajoutez votre propre vérification stricte ; l'avertissement est parti pour de bon
Penser que décoder c'est vérifier le décodeur accepte presque n'importe quoi et n'en dit rien une somme de contrôle qui correspond ou une signature vérifiée est la seule preuve qui compte
Journaliser ce que vous décodez le format ne cache rien, et le fichier de log est exactement là que la personne suivante le trouvera gardez les secrets décodés hors des logs, des alertes, et des dévers de débogage

Les bonnes habitudes

Les habitudes qui assurent que le Base64 ne prend jamais le dessus sur vos scripts :

  • Attendez des octets, toujours. Écrivez du code qui sait que decode_base64 renvoie des octets bruts, et enchaînez le decode du charset explicitement au lieu d'espérer que le terminal fera le bon choix.
  • Nommez votre charset. Partez sur UTF-8 par défaut et ne changez que si les données disent le contraire. L'échec strict de decode("UTF-8", ..., Encode::FB_CROAK) est une qualité : il vous dit que les octets ne sont pas ceux que vous supposiez.
  • Alignez l'alphabet sur la source. decode_base64url pour les URL, les tokens et les IDs ; decode_base64 pour tout le reste. Les deux alphabets ne sont pas interchangeables, et le décodeur ne vous dira pas quand vous vous trompez.
  • Validez avant de décoder. Il n'y a pas de drapeau de mode strict dans ce module, donc une petite vérification est le vigile.
  • Avalez les fichiers par défaut. -0777 ou local $/ = undef supprime une classe entière de bogues de points d'enveloppe, et le coût mémoire n'est pas un problème pour les fichiers que vous décodez vraiment.
  • Utilisez :raw sur chaque poignée de fichier. Binaire en entrée, binaire en sortie. Les calques texte sont pour les humains, pas pour les octets.
  • Vérifiez avec un hash. Quand l'original est disponible, une somme de contrôle qui correspond est la seule preuve d'un décodage exact à l'octet près.
  • Ne journalisez jamais ce que vous décodez. Le format ne cache rien.

Une brève histoire, racontée par le changelog

Le format est ancien, et la relation de Perl avec lui est plus ancienne qu'elle n'en a l'air. Quelques dates vérifiées, dans l'ordre :

  • Le code C est antérieur à Perl 5. Le décodeur rapide à l'intérieur du module descend de code de metamail, le programme de courrier de Bellcore, sous copyright en 1991, trois ans avant la première sortie de Perl 5. Quand vous appelez decode_base64 aujourd'hui, un bout des années 90 fait le travail.
  • Né dans les outils web. Le module a commencé comme LWP::Base64 dans libwww-perl au milieu des années 90, écrit par Martijn Koster et Joerg Reichelt, et il a fait son entrée en distribution CPAN à part, MIME::Base64, en avril 1997, version 2.00, avec une entrée de changelog qui dit based on libwww-perl-5.08.
  • L'ère des avertissements. Depuis la 2.03 en 1997, une entrée tronquée produisait un avertissement Premature end of base64 data sous -w au lieu d'un croak, et la 2.11 en 1999 a corrigé les builds qui avertissaient sur des données sans problème. C'était une décennie plus nerveuse pour les décodeurs.
  • Dans le core depuis 2002. Perl 5.8 a tiré le module dans la distribution core, et la synchronisation 2.13 avec le core ce même mois de décembre a apporté le support EBCDIC, c'est pourquoi l'encodeur et le décodeur fonctionnent encore sur mainframes.
  • Le dialecte sûr pour les URL est arrivé dans le core de Perl en 2010. La version 3.11 a ajouté decode_base64url et sa sœur, quatre ans après que le module autonome MIME::Base64::URLSafe ait posé ses valises sur CPAN en 2006, la même année où la RFC 4648 a codifié le dialecte.
  • Le grand silence. Cette même sortie 3.11 a retiré jusqu'à l'ancien avertissement de troncature, au cas où l'entrée suspecte serait délibérée - et chaque sortie depuis, y compris la ligne 3.16 actuelle de 2020, a gardé le décodeur poli et silencieux.

Faits insolites, version Perl

Pour boucler la visite, les trucs qui rendent cette histoire bonne :

  • Décodez le nom du format lui-même. decode_base64("YmFzZTY0") renvoie base64. C'est vrai depuis 1997 et le sera toujours.
  • Le décodeur est un fantôme poli. Dans son histoire 3.x, il n'a jamais levé d'exception sur une mauvaise entrée. Corrompu, tronqué, mauvais alphabet : il décode tout et ne se plaint de rien, un comportement que le changelog a scellé exprès en 2010.
  • L'enveloppe MIME est un multiple de quatre exprès. La limite de 76 caractères, ce sont dix-neuf groupes de trois octets, 57 octets au total, multipliés par quatre caractères, c'est pourquoi un décodage ligne par ligne est sûr sur n'importe quel corps MIME correctement enveloppé et risqué sur tout le reste.
  • uuencode n'imprime jamais de lettre minuscule. Son alphabet s'arrête au tiret bas, c'est pourquoi les vieux fichiers uuencodés ont l'air tapés par une machine toutes majuscules, et pourquoi Perl porte encore un décodeur intégré pour un format plus vieux que l'internet.
  • Perl a un temps livré sa propre commande decode-base64. Les sorties de la 2.14 en 2003 à la 3.05 en 2004 incluaient encode-base64, decode-base64, et leurs jumelles quoted-printable en scripts ; la 3.06 en 2005 les a déplacées dans la distribution séparée MIME-Base64-Scripts. Si vous trouvez une vieille installation avec cette commande sur le PATH, vous savez maintenant d'où elle vient.
  • Les ID de vidéo YouTube sont du base64url déguisé. L'ID de onze caractères dans votre barre d'adresse est un nombre de 64 bits dans l'alphabet sûr pour les URL avec le padding retiré, donc chaque vidéo que vous avez jamais regardée a une chaîne Base64 dans son URL, et decode_base64url en lit une.
  • L'indulgence est une norme, pas un bogue. La règle MIME d'être libéral dans ce qu'on accepte est la raison pour laquelle ce décodeur survit à trois décennies de données brouillon, et la raison pour laquelle la RFC 4648 met en garde : la même indulgence peut se muer en canal couvert si vous faites confiance à des entrées non fiables.

Alors la prochaine fois qu'une chaîne de lettres, de chiffres, de plus et de slash atterrit dans votre terminal, vous connaissez toute l'histoire. Un appel de fonction fait le travail, le décodeur est un fantôme poli qui ne vous refusera jamais rien, le base64url a son propre décodeur, le charset est une décision que vous prenez exprès, les fichiers entrent bruts et sortent bruts, et un hash est la seule preuve qui compte. Et si un jour vous devez faire le trajet dans l'autre sens, envelopper vos propres données brutes dans une enveloppe de texte et les envoyer au monde, l'article lié sur l'encodage Base64 en Perl, ci-dessous, couvre ce rituel avec la même profondeur.

Dernière mise à jour : 2026-09-08

Article associé : Encodage Base64 en Perl : un guide complet