Werk je met Base64-indeling? Dan is deze site perfect voor jou! Gebruik onze handige online tool om je gegevens te coderen of te decoderen.

Base64-decodering in Perl: een complete gids

Je krijgt een tekenreeks van letters, cijfers en hier en daar een + of / in je handen gedrukt, en diep van binnen weet je dat het niet is wat het lijkt. Misschien is het een token dat in een Authorization-kop reist, een .b64-bestand dat uit een supportticket is opgegraven, een certificaat in zijn -----BEGIN-pantser, of een blob die rustig in een config-bestand zit. Je opent een terminal, je typt perl, en één vraag neemt alles over: hoe krijg ik de echte data terug?

Het antwoord is klein en geruststellend. Perl levert al sinds 2002 een Base64-module mee met de taal zelf, en één functieaanroep, decode_base64, doet het hele werk: niets te installeren, niets te configureren. Even herhalen terwijl de koffie brouwt: Base64 schrijft elke drie bytes data om naar vier tekens uit een alfabet van 64 symbolen, en maakt de staart af met één of twee =-tekens, zodat het resultaat altijd op een veelvoud van vier uitkomt. Daarom is de gecodeerde vorm meestal zo'n 33 procent groter dan waar je mee begon. De startpagina van deze site legt het formaat volledig uit, dus besteedt deze gids alle tijd aan de Perl-kant van het hek: de regels van de decoder, de dialecten en de formats uit de echte wereld die je daadwerkelijk zult tegenkomen.

Het gereedschap: vijf functies, nul installaties

Elke aanroep die je nodig hebt zit in MIME::Base64, al sinds Perl 5.8 onderdeel van de core-verdeling, dus het is aanwezig op elke serieuze installatie, van de ingebouwde in routerfirmware tot die op een database-server. De check is één regel:

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

Hier is de decoderingskant van de module, in zijn geheel:

Functie Wat het doet Opmerkingen
decode_base64($str) de hoofdrol in dit artikel: zet een Base64-blob om naar ruwe bytes negeert stilletjes elk teken buiten het alfabet, voor altijd
MIME::Base64::decode($str) dezelfde decoder, aangeroepen zonder import de vorm die je in talloze oudere scripts zult tegenkomen
decode_base64url($str) decodeert het URL-veilige dialect met - en _, met of zonder vultekens toegevoegd in 3.11 in 2010; de functie die JWT's leest
MIME::Base64::decoded_base64_length($str) vertelt je hoe groot de gedecodeerde data wordt, zonder te decoderen niet standaard geëxporteerd, handig om buffers vooraf te dimensioneren
unpack("u", $data) decodeert uuencoded-data, het voor-Base64-formaat ingebouwd in Perl zelf, geen module nodig

De versie-overzichtstabel voor die functies, mocht je een vloot oude kasten moeten onderhouden:

Kenmerk Beschikbaar sinds
decode_base64() met de C-snelheidsroute Perl 5.8 in 2002, toen de module de core binnenkwam
decoded_base64_length() module 3.10 in 2010
decode_base64url() module 3.11 in 2010
Stille decodering, geen waarschuwingen bij twijfelachtige invoer module 3.11 in 2010
De huidige 3.16-reeks 2020, vereist Perl 5.6 of nieuwer

Mocht je systeem-Perl de module om een of andere reden missen, en dat zou niet moeten, dan is de oplossing één van twee regels: het distributie-pakket libmime-base64-perl op Debian en Ubuntu, of cpanm MIME::Base64 om de actuele release van CPAN te halen, waar de module al sinds haar core-dagen als dual-life-pakket woont. Voor de zeldzame machine zonder C-compiler biedt de zuiver-Perl-tweeling MIME::Base64::Perl op CPAN dezelfde basisinterface, een paar keer langzamer maar goed genoeg voor alles behalve bulkwerk. Dat is het hele verhaal van de afhankelijkheden: niets anders.

Een decoder die nooit nee zegt

Het contract is één regel lang. Reik je een tekenreeks aan, en het reikt je de gedecodeerde bytes terug als een gewone Perl-tekenreeks met ruwe octetten. Geen objecten, geen uitzonderingen, geen flags. De documentatie zet de twee regels die zijn persoonlijkheid bepalen in één zin: elk teken dat niet deel uitmaakt van de Base64-deelverzameling van 65 tekens wordt stilletjes genegeerd, en elk teken dat na een =-vulteken optreedt, wordt nooit gedecodeerd. Die beleefdheid is het belangrijkste van dit artikel, dus laat het een keer voor je werken:

use MIME::Base64 qw(decode_base64);
print decode_base64("TWFu!"),   "\n";  # Man - de uitroeptekens verdwijnen spoorloos
print decode_base64("TWFu=XX"), "\n";  # Man - alles na = wordt overgeslagen
print decode_base64("TQ"),      "\n";  # M - geen waarschuwing, geen commentaar
print decode_base64("T"),       "\n";  # de lege tekenreeks, nog steeds geen klacht

De laatste twee regels zijn de tolerantie op haar uiterste. TQ draagt één volledige byte plus vier overtollige bits, en de decoder houdt simpelweg de byte en gooit de rest weg. T draagt nog niet eens één volledige byte, dus is het resultaat leeg. Er is geen strict-modus en geen validator in de module die de ouderwetse pedanterij kan herstellen: sinds versie 3.11 in 2010 geeft decode_base64 zelfs geen waarschuwing meer bij afgekapte invoer, en oudere versies leverden een Voortijdig einde van de base64-data-waarschuwing onder -w. Is de blob fout, dan wordt er toch gedecodeerd, wat van jou de kwaliteitspoort maakt.

Hier is het tolerantiebeleid op één plek, zodat je het geheel in één oogopslag kunt zien:

Invoer Resultaat Waarom
"TWFu" Man schone invoer, de gelukkige route
"TWFu!" Man het uitroepteken zit niet in het alfabet, dus wordt het overgeslagen
"TWFu=XX" Man niets na de vultekens wordt ooit gedecodeerd
"TWFuIFdvcmxkIQ==" Man World! witruimte overal is gratis
"TQ" M één volledige byte past, de overtollige bits worden stilletjes weggeworpen
"T" de lege tekenreeks nog niet eens één volledige byte, en ook geen waarschuwing
"ab-cd_efgh" stilletjes fout bytes de URL-veilige letters worden als ruis weggegooid, de klassieke val

Die laatste regel is degene die je moet onthouden. Een base64url-segment dat je aan de standaarddecoder geeft, faalt niet: het decodeert naar rommel die er plausibel uitziet, want de -- en _-tekens worden behandeld als vreemde ruis, terwijl de overgebleven letters nog steeds geldige groepen vormen. De decoder is een getuige, geen poortwachter, dus als de invoer niet te vertrouwen is, valideer je hem zelf. Een kleine strikte check is alles wat het kost:

sub strict_base64 {
  my ($blob) = @_;
  $blob =~ s/[\r\n]//g;  # de decoder negeert deze, dus doen wij dat ook
  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 - verkeerd aantal vultekens
print strict_base64("ab-cd"), "\n";  # 0 - URL-veilig alfabet

Eén klein woord over die regex, uit een hard opgedane les: als een sub eindigt met een kale return $x =~ /.../ en het resultaat van de mislukte match rechtstreeks in printf wordt gevoed, dan geeft Perl een misleidende Ontbrekend argument in printf-waarschuwing in plaats van een nette nul. Forceer de match met ? 1 : 0 voordat je terugkeert, zoals de functie hierboven doet, en de greep verdwijnt helemaal.

Eerst bytes, daarna karakters

Onthoud wat decode_base64 teruggeeft: ruwe bytes, een gewone tekenreeks zonder gezette UTF-8-flag. Wat die bytes betekenen is een besluit dat alleen jij kunt nemen, en het is de stap waar Unicode mensen omver brengt. Perl bijhoudt of een tekenreeks karakters of bytes bevat, en length(), substr() en de meeste reguliere expressies gedragen zich anders afhankelijk van het antwoord. De oplossing is om je codering bewust aan te geven, met de Encode-module die bij elke Perl-installatie zit:

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

Dat paar getallen is de hele les. De blob is 13 bytes maar slechts 12 karakters, want de accentletter neemt in UTF-8 twee bytes in beslag. Sla de tekensetstap over en de bytes printen nog netjes naar een UTF-8-terminal, en precies daarom blijft de fout onzichtbaar totdat een tekenreeksfunctie ze telt, of de bytes door een pipeline reizen die karakters verwacht. Twijfel je, dan decodeer je met een strenge tekenset en laat je de exceptie je de waarheid over de bytes vertellen: geef Encode::FB_CROAK door en decode() crasht bij ongeldige sequenties in plaats van de standaard stille U+FFFD-substitutie, en dat is een feature.

De korte lijst met tekensets die je daadwerkelijk zult gebruiken:

Tekenset Wanneer je het gebruikt Waar je op moet letten
UTF-8 de standaardveronderstelling: API's, JSON, webinhoud, moderne tekst ongeldige sequenties worden standaard U+FFFD; met Encode::FB_CROAK crasht het netjes, en dat is precies wat je wilt
Latin-1 ouderwesterse tekst, één byte per teken, kan nooit falen het verminkt UTF-8 met plezier tot dubbel gecodeerde mojibake
ASCII data waarvan je zeker weet dat het gewone 7-bit-tekst is elke byte boven 127 wordt standaard U+FFFD (crasht onder Encode::FB_CROAK)
UTF-16 Windows-tekst, waar de byte-order-mark de bytevolgorde bepaalt de BOM is de enige aanwijzing voor de bytevolgorde, dus houd hem in de bytes

En hier is de val waar de tekensetstap je voor beschermt. Als de bytes die je decodeerde al UTF-8 zijn en je stuurt ze onderweg naar buiten door encode("UTF-8", ...), dan krijg je geen kopie: je krijgt een dubbele codering, waar elke accentletter opzwellt tot twee karakters op zich. Het klassieke symptoom is tekst die Hëllo lees en nu Hëllo leest, en de ontvanger aan de andere kant van de draad decodeert dat trouw. Bytes in, bytes uit, één benoemde omzetting ertussen.

base64url: het alfabet voor URLs en tokens

De helft van de Base64 die het moderne internet overstekt, is helemaal niet het standaardalfabet. Het +-teken is hoe een browser een spatie encodeert in een query string, en / is een pad-scheidingsteken, dus zijn de standaardletters een ramp in URLs. RFC 4648, sectie 5, definieert de oplossing: een tweede alfabet dat + en / ruilt voor - en _, en per conventie ook de =-vultekens en de regeleinden weglaat. De RFC is expliciet dat deze codering niet als hetzelfde als de base64-codering beschouwd moet worden, en Perl heeft al sinds versie 3.11 in 2010 een eigen paar functies hiervoor:

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

Twee dingen om te weten. Eerst is decode_base64url tevreden met invoer zonder vultekens, en dat is de vorm die je in de praktijk daadwerkelijk zult vinden, dus het ritueel van eerst-de-vultekens-terughalen dat andere talen eisen, geldt hier niet; invoer mét vultekens werkt ook. Ten tweede is de standaarddecoder een ander dier: geef hem een base64url-segment en je krijgt stilletjes fout bytes, want de -- en _-tekens worden als ruis weggegooid en de rest decodeert nog steeds. Gebruik de juiste decoder, of normaliseer handmatig als je vastzit op een legacy-codepad:

my $seg = "ab-cd_efgh";
$seg =~ tr{-_}{+/};                    # de URL-veilige letters, terugvertaald naar huis
$seg .= "=" x (-length($seg) % 4);     # vultekens hersteld voor de standaarddecoder
my $raw = decode_base64($seg);
print unpack("H*", $raw), "\n";  # 69bf9c77f79f82 - zeven bytes, terug in het standaardalfabet

Je zult base64url meteen tegenkomen in JWTs, de tokens die elke moderne API uitdeelt, en in elk ondoorzichtig ID dat in een URL leeft: video-ID's van elf tekens, UUID's opgeslagen in het URL-veilige alfabet (CPAN heeft Data::UUID::Base64URLSafe precies hiervoor), en database-sleutels die een adresbalk moeten overleven. En als je op een oude Perl zit die vóór de core-functies dateert, dan biedt de losse MIME::Base64::URLSafe-module uit 2006, een port van Pythons urlsafe-codec, urlsafe_b64encode en urlsafe_b64decode; op alles vanaf 3.11 zijn de ingebouwde functies de betere keuze.

JWT's: de kop en de payload lezen

Een JSON Web Token is structureel twee stukken JSON die een vermomming dragen, plus een cryptografische ontvangstbevestiging. De compacte vorm uit RFC 7515 is drie base64url-segmenten aan elkaar gekoppeld met punten: de beschermde kop, de payload en de handtekening. Eensplitsen en lezen is drie regels:

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)

Let op de taakverdeling: decode_base64url zet elk segment om naar bytes, en decode_json uit de core-JSON::PP-module, aanwezig sinds Perl 5.14, zet de bytes van kop en payload om naar Perl-datastructuren. Het segment met de handtekening is ook base64url, maar het is een cryptografische digest, dus decodeer je nooit meer dan de eerste twee segmenten en laat je een degelijke bibliotheek het derde afhandelen.

De val is degene die iedereen vergeet: leesbaar betekent niet geldig. De kop en de payload zijn opzettelijk leesbaar, wat ook betekent dat iedereen ze kan herschrijven; de handtekening is het enige bewijs. Voor iets waar je om geeft: verifieer, decodeer niet alleen. De CPAN-module Crypt::JWT, die bouwt op CryptX, doet het hele werk:

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

Het crasht bij een slechte handtekening, en het vastpinnen van accepted_alg sluit het gat van algorithmeverwarring waarin een aanvaller de token omschakelt naar een zwakkere variant. Een decodeer-en-print-workflow is prima om een token te inspecteren tijdens een supportgesprek; het is geen authenticatie.

Bestanden: van de .b64 terug naar het origineel

Bestanden zijn waar de one-liner-cultuur van Perl echt gaat schitteren, en het hele werk past in één commando. De -0777-flag is het geheime ingrediënt, want het slurpt het hele bestand in als één tekenreeks in plaats van de decoder regel voor regel te voeden:

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

De regel-voor-regel-vorm is veilig voor één specifieke klasse bestanden: die waarin elke regel een veelvoud van vier Base64-tekens bevat, en dat geldt voor elk correct MIME-omwikkelde lichaam, want 76 is een veelvoud van 4. Het moment dat de omwikkelpunten scheef gaan - en in handmatig omwikkelde bestanden is dat meestal zo - begint het regel-voor-regel-decoderen vultekens te produceren in het midden van de data. Slurp-modus kent zo'n voorwaarde niet, en daarom is het de standaardkeuze:

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

In een script is het patroon de standaard Perl-bestandsdans, met één stille maar belangrijke detail: de :raw-layers op beide handles, zodat Perl de bytes nooit als platform-tekst gaat interpreteren bij het binnen- of buitengaan:

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";  # vergelijk met de checksum van de afzender
open my $out, ">:raw", $ARGV[1] or die $!;
print {$out} $decoded;
close $out;

De hashregel doet meer dan pochen. Omdat de decoder bijna alles accepteert, is een matchende checksum met wat de afzender publiceerde het enige bewijs dat de reis byte-exact was. Voor écht gigantische bestanden is de regel-voor-regel-loop de laag-geheugen-alternatief, mits de omwikkelingen op vier-teken-grenzen liggen, en MIME::Base64::decoded_base64_length vertelt je hoe groot de uitvoer wordt vóórdat je je aan een buffer verbindt.

PEM-pantsering: haal het jasje af, behoud het DER

De .pem-bestanden in elke beveiligingsstack zijn dezelfde Base64, dan gepantserd: een headerregel, een footerregel, en een lichaam dat per de oude Privacy Enhanced Mail-conventie is omwikkeld op 64 tekens. De omhulling is het enige interessante deel, want de decoder van de module maakt zich helemaal geen zorgen over regellengtes:

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

De BEGIN- en END-regels worden eraf gehaald, de rest wordt samengevoegd tot één tekenreeks, en elke regeleinde wordt onderweg genegeerd. Voor dagelijkse certificaatwerkzaamheden doet de OpenSSL-tooling dit al voor je; de acht regels hierboven zijn het patroon om te onthouden als je de ruwe DER-bytes zelf nodig hebt, voor een hash, een vingerafdruk of een vergelijking.

Data-URI's: afbeeldingen die hun eigen adres meedragen

Het data:-schema uit RFC 2397 plaatst een payload direct inline in een URL: data:, een optionele media-type, een optionele ;base64-flag, een komma, en de data. Binair media zoals afbeeldingen gebruiken de flag, dus is de payload het standaardalfabet met vultekens, en kan de gewone decoder het na een kleine knip:

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: de magische bytes van PNG

De magische bytes checken is de zet. Als die eerste acht hexadecimale tekens niet 89504e47 zijn, dan is de afbeelding geen PNG, wat de media-type ook beweert, en een decoder die nooit klachten maakt, maakt precies die soort stille leugens mogelijk.

Neven en fossielen: uuencode en de andere alfabetten

Voordat Base64 won, was de klassieke UNIX-methode om een binair bestand per mail te sturen uuencode, en je zult het nog steeds tegenkomen in oude mailinglists en oude tools. Het goede nieuws: Perl heeft een ingebouwde decoder voor, geen module nodig, dankzij de u-template in pack en unpack:

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

De twee aanroepen zijn exacte omgekeerden van elkaar, en dat is het hele verhaal dat je nodig hebt, en het klassieke uuencode-commando uit de UNIX-toolchain wikkelt de kale regels simpelweg in een begin-header en een end-footer, dus de payload die je decodeert is het deel ertussen.

Base64 heeft ook dialect-neven, en weten welke decoder welke opet, spaart je een debugsessie:

Dialect Omwikkeling Waar je het tegenkomt Wat decode_base64 doet
MIME (RFC 2045) 76 tekens e-maillichamen decodeert het zoals het is: regeleinden en CRLF worden genegeerd
PEM (RFC 1421) 64 tekens certificaten en sleutels decodeert het zoals het is
PKIX (RFC 7468) 64 tekens X.509-tekststructuren decodeert het zoals het is
OpenPGP-pantsering (RFC 9580) 76 tekens plus een CRC24-regel PGP-sleutels en handtekeningen decodeert het zoals het is, de checksumregel wordt simpelweg genegeerd
IMAP (RFC 3501) geen mailboxnamen niet dit alfabet: de slash wordt een komma, vertaal eerst de letters

De les: voor elke variant van het standaardalfabet die alleen anders omwikkelt, dekt één tolerante decoder ze allemaal. Alleen wanneer het alfabet zelf verandert, moet je de tekens eerst vertalen.

Config, databases en omgevingsvariabelen

Containerplatforms, cloud-console's en een verrassend aantal configuratiebestanden bewaren authenticatiegegevens en kleine documenten als ondoorzichtige Base64-tekenreeksen, want een blob van letters en cijfers oogt minder gevaarlijk dan het wachtwoord dat het is. Het decoderen is altijd dezelfde twee stappen: decode_base64 plus een tekensetbeslissing:

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

De reden waarom het formaat op deze plek zo populair is, is precies die waar RFC 4648 voor waarschuwt: mensen stoppen met op te merken dat de data leesbaar is. Behandel de gedecodeerde uitvoer dus als vertrouwelijk vanaf het moment dat ze wordt teruggegeven, en houd zowel de blob als het resultaat uit logbestanden, meldingen en debug-dumps.

Dezelfde vorm verschijnt in databases, waar binaire data vaak als Base64 in een TEXT-kolom reist, omdat de kolom niet kan beloven willekeurige bytes onaangetast door te laten:

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;

E-mail: MIME-delen en bijlagen

E-mail is waar Base64 zijn naam vandaan haalt, en de tolerantie van de module is ontworpen voor precies dit verkeer. Een MIME-deel met Content-Transfer-Encoding: base64 arriveert als regels van 76 tekens aan CRLF afgesloten tekst, en de decoder kauwt de hele envelop zoals ze is op, regeleinden inbegrepen:

use MIME::Base64 qw(decode_base64);
my $part_body = "SGVsbG8sIHF1ZXJ5IQpUaGlzIE1JTUUgcGFydCB0cmF2ZWxsZWQgYXMgYmFzZTY0LCB3cmFwcGVk";
$part_body .= "\r\nIGF0IDc2IGNoYXJhY3RlcnMsIENSTEYgYmV0d2VlbiBsaW5lcy4=";
my $text = decode_base64($part_body);
print $text;  # de oorspronkelijke berichttekst van twee regels

Als je met een framework mail bouwt of ontleedt, dan doe je niets hiervan handmatig: MIME::Lite base64-codeert een bijlage voor je als je Encoding => "base64" doorgeeft aan attach, en Email::MIME doet hetzelfde automatisch. De handgemaakte versie hierboven is voor de mail die als rauwe tekst arriveert in een log, een ticket of een doorverwezen bericht, en in de praktijk is dat een flink deel.

Valkuilen, verzameld en gerangschikt

De module is klein genoeg om te onthouden, dus hier is de complete valkuillijst op één plek, gerangschikt naar ongeveer hoe vaak ze beet:

Val Wat er gebeurt Oplossing
Een base64url-segment aan de standaarddecoder geven de - en _ worden als ruis weggegooid, en de rest decodeert naar stilletjes fout bytes gebruik decode_base64url, of vertaal eerst het alfabet en herstel de vultekens
Het zwijgen bij beschadigde invoer vertrouwen vremde tekens, afkapping en een verkeerd alfabet decoderen allemaal zonder een enkele waarschuwing voer eerst de strikte check uit, en verifieer met een hash als het origineel beschikbaar is
Niet-ASCII-karakters in de blob een dwalende accentletter of een geplakte Unicode-spatie wordt stilletjes genegeerd en krimpt het resultaat zonder commentaar dezelfde strikte check wijst alles buiten het 7-bit-alfabet af
Het resultaat als tekst behandelen de bytes dragen geen UTF-8-flag, dus telt length() bytes en krijgen tekenreeksfuncties het verkeerde beeld koppel decode("UTF-8", $raw) of je gekozen tekenset erachter vóór welke tekstverwerking dan ook
Dubbele codering onderweg naar buiten al-UTF-8-bytes door encode("UTF-8", ...) sturen maakt van Hëllo Hëllo codeer karakters, nooit ruwe bytes, en check de flag met utf8::is_utf8() als je twijfelt
Een bestand regel voor regel decoderen met scheef omwikkelde regels regels die niet eindigen op vier-teken-grenzen produceren vultekens in het midden van de uitvoer slurp met -0777, of garandeer omwikkelpunten op vier tekens
Een mislukte regex-match in een numerieke context teruggeven een sub die eindigt met return $x =~ /.../ en dat resultaat in printf voedt, geeft een misleidende Ontbrekend argument in printf-waarschuwing forceer de match: return $x =~ /.../ ? 1 : 0
Oude code die de oude waarschuwing verwacht scripts van vóór 3.11 die leunden op de Voortijdig einde van de base64-data-waarschuwing onder -w zien nu niets voeg je eigen strikte check toe; de waarschuwing is definitief weg
Aannemen dat decoderen verifiëren is de decoder accepteert bijna alles en zegt daar niets over een matchende checksum of een geverifieerde handtekening is het enige bewijs dat ertoe doet
Opschrijven wat je decodeert het formaat verbergt niets, en het logbestand is precies waar de volgende persoon het vindt houd gedecodeerde geheimen uit logs, meldingen en debug-dumps

Goede gewoontes

De gewoontes die voorkomen dat Base64 ooit de bovenhand neemt over je scripts:

  • Verwacht bytes, altijd. Schrijf code die weet dat decode_base64 ruwe octetten teruggeeft, en koppel de tekenset-decode expliciet erachter in plaats van te hopen dat de terminal het goede doet.
  • Noem je tekenset. Standaard UTF-8 en wissel alleen als de data anders zegt. De strikte fout van decode("UTF-8", ..., Encode::FB_CROAK) is een feature: het vertelt je dat de bytes niet zijn wat je veronderstelde.
  • Pas het alfabet aan de bron aan. decode_base64url voor URLs, tokens en ID's; decode_base64 voor alles anders. De twee alfabetten zijn niet onderling verwisselbaar, en de decoder zal je niet vertellen dat je het verkeerd hebt gekozen.
  • Valideer vóór je decodeert. Deze module heeft geen strict-modus-flag, dus is een kleine check de poortwachter.
  • Slurp bestanden standaard. -0777 of local $/ = undef haalt een hele klasse omwikkelpunt-bugs weg, en de geheugenprijs is geen probleem voor de bestanden die je daadwerkelijk decodeert.
  • Gebruik :raw op elke filehandle. Binair in, binair uit. Tekst-layers zijn voor mensen, niet voor bytes.
  • Verifieer met een hash. Als het origineel beschikbaar is, is een matchende checksum het enige bewijs van een byte-exacte decode.
  • Log nooit wat je decodeert. Het formaat verbergt niets.

Een korte geschiedenis, verteld door de changelog

Het formaat is oud, en de relatie van Perl ermee is ouder dan het lijkt. Enkele geverifieerde data, in volgorde:

  • De C-code is ouder dan Perl 5. De snelle decoder in de module stamt af van code in metamail, het mailprogramma van Bellcore, met copyright uit 1991, drie jaar vóór de eerste Perl 5-release. Als je vandaag decode_base64 aanroept, doet een stukje jaren negentig het werk.
  • Geboren in de webtools. De module begon als LWP::Base64 in libwww-perl in het midden van de jaren negentig, geschreven door Martijn Koster en Joerg Reichelt, en promoveerde in april 1997 naar een eigen CPAN-distributie, MIME::Base64, versie 2.00, met de changelog-vermelding gebaseerd op libwww-perl-5.08.
  • Het tijdperk van de waarschuwingen. Vanaf 2.03 in 1997 gaf afgekapte invoer een Voortijdig einde van de base64-data-waarschuwing onder -w in plaats van een croak, en 2.11 in 1999 corrigeerde de builds die waarschuwden bij data die prima was. Het was een zenuwachtiger decennium voor decoders.
  • Core sinds 2002. Perl 5.8 trok de module in de core-distributie, en de 2.13-sync met de core diezelfde december bracht EBCDIC-ondersteuning mee, en daarom werken de encoder en decoder nog steeds op mainframes.
  • Het URL-veilige dialect kwam in 2010 in de Perl-core binnen. Versie 3.11 voegde decode_base64url en zijn zusfunctie toe, vier jaar nadat de losse MIME::Base64::URLSafe-module in 2006 op CPAN was geland, hetzelfde jaar dat RFC 4648 het dialect codificeerde.
  • Het stillen. Diezelfde 3.11-release haalde zelfs de oude afkappingswaarschuwing weg, voor het geval de twijfelachtige invoer opzettelijk was - en elke release sindsdien, waaronder de huidige 3.16-reeks uit 2020, heeft de decoder beleefd en stil gehouden.

Grappige feiten, specifiek over Perl

Ter afsluiting van de rondleiding, de weetjes die dit verhaal een goed verhaal maken:

  • Decodeer de eigen naam van het formaat. decode_base64("YmFzZTY0") geeft base64. Dat klopt sinds 1997 en blijft voor altijd kloppen.
  • De decoder is een beleefde geest. In haar 3.x-geschiedenis heeft het nooit een uitzondering gegeven bij slechte invoer. Beschadigd, afgekapt, verkeerd alfabet: het decodeert alles en klaagt over niets, een gedrag dat de changelog in 2010 opzettelijk in beton gegoten heeft.
  • De MIME-omwikkeling is met opzet een veelvoud van vier. De limiet van 76 tekens is negentien groepjes van drie bytes, 57 bytes in totaal, keer vier tekens, en daarom is regel-voor-regel-decoderen veilig bij elk correct omwikkelde MIME-lichaam en onveilig bij alles anders.
  • uuencode print nooit een kleine letter. Zijn alfabet eindigt bij de onderstreping, en daarom zien oude uuencoded-bestanden eruit alsof ze getypt zijn door een machine die alleen hoofdletters kent, en daarom draagt Perl nog steeds een ingebouwde decoder voor een formaat dat ouder is dan het internet.
  • Perl leverde ooit een eigen decode-base64-commando mee. Releases van 2.14 in 2003 tot 3.05 in 2004 bundelden encode-base64, decode-base64 en hun quoted-printable-tweelingen als scripts; 3.06 in 2005 verplaatste ze naar de aparte MIME-Base64-Scripts-distributie. Als je een oude installatie vindt met dat commando op de PATH, dan weet je nu waar het vandaan komt.
  • YouTube-video-ID's zijn vermomde base64url. De ID van elf tekens in je adresbalk is een 64-bits getal in het URL-veilige alfabet met de vultekens eraf, dus elke video die je ooit hebt bekeken heeft een Base64-tekenreeks in zijn URL, en decode_base64url kan er een lezen.
  • De tolerantie is een standaard, geen bug. De MIME-regel om soepel te zijn in wat je aanneemt, is de reden dat deze decoder drie decennia rommelige data overleeft, en de reden dat RFC 4648 waarschuwt dat dezelfde tolerantie in een verborgen kanaal kan worden omgezet als je onbetrouwbare invoer vertrouwt.

Dus de volgende keer dat een tekenreeks van letters, cijfers, plustekens en slashes in je terminal landt, ken je het hele verhaal. Eén functieaanroep doet het werk, de decoder is een beleefde geest die je nooit zal afwijzen, base64url heeft een eigen decoder, de tekenset is een besluit dat je bewust neemt, bestanden komen rauw in en gaan rauw uit, en een hash is het enige bewijs dat ertoe doet. En als je ooit de reis in de andere richting moet maken, je eigen ruwe data in een tekst-envelop verpakken en de wereld in sturen, dan dekt het gerelateerde artikel over Base64-coderen in Perl, hieronder gelinkt, dat ritueel in dezelfde diepte.

Laatst bijgewerkt: 2026-10-06

Gerelateerd artikel: Base64-codering in Perl: een complete gids