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-codering in Perl: een complete gids

Je hebt data die een kanaal moet overleven dat het niet bevalt. Een binaire blob die in een JSON-veld moet zitten. Een afbeelding die binnen een HTML-tag moet leven. Een certificaat dat thuishoort in een config-bestand. Een token dat URLs, koppen en query strings door zal reizen. Dat is het dagelijks leven van Base64-codering: het schrijft elke drie bytes ruwe data om naar vier tekens uit een alfabet van 64 letters, met één of twee =-tekens die de staart afronden, zodat het resultaat gewone tekst is die alles kan meedragen, meestal zo'n 33 procent langer dan waar je mee begon. De startpagina van deze site legt het formaat volledig uit, dus dit artikel richt zich op wat Perl je geeft, wat het stilletjes voor je beslist, en waar de valkuilen zitten.

Het goede nieuws eerst: encode_base64 woont al sinds 2002 in de core van Perl, het is geïmplementeerd in C, en het is comfortabel snel. Het interessante is dat de functie meningen heeft. Het wikkelt zijn uitvoer om op 76 tekens, het voegt een regeleinde toe aan het eind, en het encodeert geen Unicode-karakters die je eerst niet naar bytes hebt omgezet: boven het Latin-1-bereik crasht het gewoonweg, en onder die lijn veronderstelt het stilletjes Latin-1-bytes. Deze gids loopt alle smaken Base64 door die een Perl-ontwikkelaar daadwerkelijk produceert: de one-liner, het MIME-omwikkelde e-maillichaam, de PEM-omwikkelde sleutel, het URL-veilige token, en de streaming-versie voor bestanden die te groot zijn om in het geheugen te houden.

Eén functie, een verborgen regeleinde

De complete API, exact zoals de moderne documentatie die presenteert:

encode_base64( $bytes )
encode_base64( $bytes, $eol )

Lees dat nog eens. Twee argumenten, waarvan er één optioneel is, één terugkeerwaarde, geen flags. Het optionele tweede argument is de regeleinde-sequentie, en die is standaard een gewone nieuwe regel, wat betekent dat de onschuldigste aanroep van Perl omwikkelde, met een regeleinde afgesloten uitvoer produceert:

use MIME::Base64 qw(encode_base64);
my $wrapped = encode_base64("Aladdin:open sesame");
print length($wrapped), "\n";  # 29: de 28 tekens plus een nieuwe regel
my $single = encode_base64("Aladdin:open sesame", "");
print length($single), "\n";   # 28: geef een lege tekenreeks voor geen omwikkeling

De uitvoergrootte volgt een vast patroon dat je vóór het aanroepen kunt voorspellen:

Invoerbytes Uitvoertekens Vultekens
0 0 geen
1 4 twee =
2 4 één =
3 4 geen
100,000 133,336 geen, op één enkele regel
3,000,000 4,000,000 geen

Het patroon is vier tekens voor elke volledige groep van drie bytes, plus een laatste gedeeltelijke groep, afgemaakt met één of twee =-tekens. Een gevolg dat de moeite waard is om te kennen: één byte en drie bytes produceren allebei vier tekens, dus de gecodeerde lengte verbergt de exacte invoergrootte. En als je de grootte nodig hebt zonder het werk te doen, dan heeft de module al sinds 3.10 in 2010 een lengtefunctie. Die is alleen niet standaard geëxporteerd, dus roep je hem aan via de pakketnaam:

use MIME::Base64 ();
my $with_wrap   = MIME::Base64::encoded_base64_length($bytes);        # regels van 76 tekens, standaard regeleinde
my $single_line = MIME::Base64::encoded_base64_length($bytes, "");    # geen omwikkeling
my $mime_body   = MIME::Base64::encoded_base64_length($bytes, "\r\n");

Er is nog één regel om te leren, want het is de enige manier waarop de encoder ooit schreeuwt: als de tekenreeks die je hem geeft karakters bevat met een code boven 255, dan crasht encode_base64 met Breed teken in subrutine-ingang. Onder die lijn is het falen stiller: karakters tot 255 worden stilletjes gedegradeerd naar hun Latin-1-bytes, dus een tekenreeks met accenttekens die de omzetting is ontlopen, codeert als Latin-1 in plaats van UTF-8, en niemand vertelt je dat. De Base64-codering is alleen gedefinieerd voor één-byte-karakters, en Perl 5.8 en nieuwer staat uitgebreide karakters in tekenreeksen toe, dus de omzetting is een besluit dat je bewust neemt, met Encode, in de volgende sectie.

Tekst of bytes? De stap die de functie niet kan doen

Perl-tekenreeksen dragen een stille flag die zegt of ze karakters of bytes bevatten, en Base64 woont aan de byteskant van die lijn. Als je tekst een tekenreeks met karakters is, en dat is het moment dat ze uit een JSON-parser, een template of een literal met accentletters in een UTF-8-bronbestand komt, weigert de encoder te raden welke bytes je bedoelde voor karakters boven het Latin-1-bereik, en het vertelt je dat ook; onder het bereik raadt het stilletjes Latin-1. De oplossing is in beide gevallen dezelfde, en het is één corefunctie uit de Encode-module:

use MIME::Base64 qw(encode_base64);
use Encode qw(encode);
my $chars = "H\x{eb}llo W\x{f6}rld";  # een tekenreeks met karakters
my $utf8  = encode("UTF-8", $chars);  # nu: bytes
my $b64   = encode_base64($utf8, "");
print $b64, "\n";  # SMOrbGxvIFfDtnJsZA==

Die encode-aanroep is de hele dans: kies de byte-voorstelling, en UTF-8 voor alles moderne, zet de karakters om naar die bytes, en geef dan pas de bytes aan de encoder. Voor oude westerse tekst die als Windows 1252 aankwam, is de omzetting dezelfde functie met een andere naam, encode("Windows-1252", $legacy), die je de oorspronkelijke één-byte-vorm oplevert. De Encode-module is core, dus kost dit alles niets.

Nu de val. Als de bytes die je hebt al UTF-8 zijn en je stuurt ze opnieuw door encode("UTF-8", ...), in de veronderstelling dat je ze UTF-8 maakt, 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 elke decoder op het internet decodeert dat trouw voor je:

use Encode qw(encode decode);
my $right = encode("UTF-8", "H\x{eb}llo");
my $wrong = encode("UTF-8", $right);  # de bytes opnieuw coderen als karakters
print decode("UTF-8", $right), "\n";  # Hëllo
print decode("UTF-8", $wrong), "\n";  # Hëllo

De vuistregel die het voorkomt: bytes worden exact één keer gecodeerd, en utf8::is_utf8() toont je aan welke kant van de lijn een tekenreeks staat. Is de flag gezet, dan houd je karakters vast en is de encode()-aanroep de juiste zet; is hij niet gezet, dan houd je bytes vast en ben je klaar voor Base64.

Regelomwikkeling: drie dialecten, één regel

Omdat de standaarduitvoer omwikkelde en met een regeleinde afgesloten is, is de eerste beslissing voor elke coderklus een bestemmingsvraag: waar zal deze tekenreeks wonen? Het verbindende feit is dat decoders regeleinden helemaal negeren: RFC 2045 zegt decoderende software alle regeleinden en tekens buiten het alfabet te negeren, dus is omwikkeling een beleefdheid voor regelgebaseerde tools en mensen, geen semantisch verschil. De drie antwoorden in de praktijk:

Geen regeleinden. De functie-uitvoer met de omwikkeling uitgeschakeld, exact één regel. Dit is wat je wilt voor URLs, JSON-payloads, koppen, database-waarden en alles anders waar een regeleinde een bug zou zijn. Dit is ook wat de meeste mensen bedoelen als ze om gewoon de Base64 vragen:

my $single = encode_base64($bytes, "");

MIME: 76 tekens plus CRLF. De e-mailconventie uit RFC 2045: gecodeerde regels mogen niet langer zijn dan 76 tekens, en de MIME-wereld spreekt CRLF. Deze is het eigen werk van de module, gedaan met het tweede argument:

my $mime_body = encode_base64($bytes, "\r\n");

PEM: 64 tekens plus LF. Sleutels en certificaten gebruiken de oudere conventie met kortere regels van 64 tekens, en de module kan die breedte niet zelf produceren, dus vult een hulpfunctie van vier regels het gat:

sub wrap_lines {
  my ($text, $width) = @_;
  return join "\n", $text =~ /(.{1,$width})/g;
}
my $pem = "-----BEGIN CERTIFICATE-----\n"
        . wrap_lines(encode_base64($der, ""), 64) . "\n"
        . "-----END CERTIFICATE-----\n";

Dezelfde hulpfunctie dient ook de andere 64-teken-dialecten - de PKIX-tekstcodering uit RFC 7468 en OpenPGP-pantsering, waarvan de datalijnen 64 tekens breed zijn en waarvan de afsluitende CRC24-checksumregel door GnuPG wordt toegevoegd in plaats van door jou. Eén valkuil geldt voor ze allemaal: encode_base64 voegt het regeleinde toe aan het allerlaatste einde van het resultaat, ook als de laatste regel exact zijn breedte vult. Tript een afnemer stroomafwaarts over die afsluitende lege regel, dan lost een rtrim-aanroep op het resultaat het op.

URL-veilige Base64: het alfabet van - en _

Het standaardalfabet bevat + en /, en beide zijn lastig buiten een tekstbestand: een + in een form-geëncodeerde query string wordt een spatie voordat je toepassing hem ooit ziet, en / is een pad-scheidingsteken in URLs. Bestandsnamen en tokens hebben hun eigen klachten. RFC 4648, sectie 5, lost dit op met het URL- en bestandsnaam-veilige alfabet, waar + wordt tot -, / wordt tot _, en de afsluitende =-vultekens meestal worden weggelaten. De RFC benadrukt dat dit niet als hetzelfde als de base64-codering beschouwd moet worden, dus behandel het als een apart formaat, meestal base64url genoemd. Perl produceert het in één aanroep al sinds versie 3.11 in 2010:

use MIME::Base64 qw(encode_base64url);
my $seg = encode_base64url("sunset-42");
print $seg, "\n";  # c3Vuc2V0LTQy: geen vultekens, geen nieuwe regel

Die ene aanroep doet alle drie de veranderingen: de alfabetwissel, geen vultekens, geen regeleinden. Houd je al standaard-Base64 vast en wil de bestemming het URL-veilige dialect, dan zetten twee tekenreeksoperaties het op zijn plaats om:

sub to_urlsafe {
  my ($b64) = @_;
  $b64 =~ tr{+/}{-_};
  return $b64 =~ s/=+\z//r;
}
my $seg = to_urlsafe(encode_base64($bytes, ""));

Wanneer gebruiken: JSON Web Token-delen, OAuth-state- en nonce-parameters, API-ID's die je in URL-paden zet, en ondoorzichtige sleutels die een adresbalk of een bestandsnaam moeten overleven, waarvoor CPAN's Data::UUID::Base64URLSafe precies bestaat. Wanneer niet gebruiken: e-maillichamen, PEM-pantsering, en elke plek waar aan de andere kant een standaardalfabet-afnemer zit, want - en _ zitten niet in hun vocabulaire. En meng de twee alfabetten niet stilletjes: een URL-veilig gecodeerde waarde moet URL-veilig gedecodeerd worden, overal, voor altijd. Op Perls ouder dan 3.11 biedt de losse MIME::Base64::URLSafe-module uit 2006, een port van Pythons urlsafe-codec, urlsafe_b64encode; op alles moderne is de ingebouwde functie het juiste gereedschap.

Een JWT bouwen: elk deel met de hand

JSON Web Tokens zijn de vlaggenschip-afnemer van Base64 in moderne API's, en ze gebruiken het URL-veilige, niet-gevulde dialect uit de sectie hierboven. Volgens RFC 7515 is een compacte JWT drie met punten gescheiden base64url-delen: de beschermde kop, de payload en de handtekening. Er een met de hand bouwen is een plezierige manier om elk bewegende deel te zien:

use MIME::Base64 qw(encode_base64url);
use JSON::PP qw(encode_json);
use Digest::SHA qw(hmac_sha256);
my $secret  = "correct-horse-battery-staple";
my $head    = encode_base64url(encode_json({ alg => "HS256", typ => "JWT" }));
my $claims  = encode_base64url(encode_json({ sub => "homer", role => "admin" }));
my $sig     = encode_base64url(hmac_sha256("$head.$claims", $secret));
my $jwt     = "$head.$claims.$sig";
print $jwt, "\n";  # een compact HS256-token; de volgorde van de sleutels in elk JSON-deel varieert van run tot run

Drie details die de moeite waard zijn om op te merken. Eerst geeft encode_json uit de core-JSON::PP-module compacte UTF-8-bytes zonder witruimte, en dat is precies wat de JOSE-specificaties in een token willen. Ten tweede is de payload voor iedereen leesbaar, en dat is met opzet: een JWT is een ondertekend biljet, geen geheim, dus stop nooit vertrouwelijke waarden in de claims. Ten derde is de handtekening de base64url-codering van ruwe HMAC-bytes, en daarom gaat hmac_sha256 rechtstreeks in de encoder zonder enige hex-opmaak.

Voor productie handrol je geen handtekening. De CPAN-module Crypt::JWT, die bouwt op CryptX, implementeert JWS en JWE met de volledige set algoritmes:

use Crypt::JWT qw(encode_jwt);
my $jwt = encode_jwt(
  payload => { sub => "homer", role => "admin" },
  alg     => "HS256",
  key     => $secret,
);

En aan de ontvangzijde, pin het algoritme vast met accepted_alg zodat een aanvaller de token niet kan omschakelen naar een zwakkere variant: decode_jwt(token => $jwt, key => $secret, accepted_alg => "HS256") verifieert de handtekening en crasht bij falen. Met de hand rollen is prima om te begrijpen; een bibliotheek is prima voor geld.

HTTP: auth-koppen, data-URI's en de WebSocket-handshake

De Authorization: Basic-kop is het oudste levende gebruiksgeval: de gebruikersnaam en het wachtwoord, met een dubbele punt aan elkaar gekoppeld, gecodeerd als één regel, met het schema-woord ervoor. Het lege tweede argument is hier draaglastend, want een afsluitend regeleinde binnen een kopveld is een bug:

use MIME::Base64 qw(encode_base64);
my $user = "alice";
my $pass = "s3cr3t";
my $header = "Basic " . encode_base64("$user:$pass", "");
print $header, "\n";  # Basic YWxpY2U6czNjcjN0

Data-URI's uit RFC 2397 zijn hetzelfde idee, toegepast op afbeeldingen: de payload zit direct in de URL, dus is er geen tweede aanvraag nodig om hem op te halen. Binair media gebruikt de ;base64-flag, dus is de payload exact wat encode_base64 produceerde met de omwikkeling uitgeschakeld:

my $uri = "data:" . $mime_type . ";base64," . encode_base64($bytes, "");

De afwegingen zijn wel reëel. De gecodeerde payload is zo'n 33 procent groter dan het bestand, en dat maakt het HTML-document zelf groter. Browsers cachen een data-URI niet zoals ze een bestands-URL cachen - er is geen aparte ophaling om te cachen, dus elke weergave van de pagina verstuurt de bytes opnieuw als deel van het document, en de RFC zelf zegt dat data-URI's alleen nuttig zijn voor korte waarden. Gebruik ze voor avatars, icoontjes en kleine inline grafieken; gebruik echte bestanden voor alles anders. Er is een derde HTTP-hoekje dat Base64 stilletjes gebruikt: de WebSocket-handshake uit RFC 6455, waar de client een Sec-WebSocket-Key-kop stuurt die de Base64 is van zestien willekeurige bytes. Frameworks zoals Mojolicious doen het voor je, maar als je het ooit op de draad ziet, dan weet je nu wat het is:

use MIME::Base64 qw(encode_base64);
my $key = encode_base64(pack("C16", map { int(rand 256) } 1 .. 16), "");
print length($key), "\n";  # 24: zestien willekeurige bytes, afgemaakt tot de groep van vier tekens

Bestanden: slurps, 57-byte-blokken en de CLI

De meest rechttoe-rechtaanse coderklus: een bestand wordt tekst. Perl-tekenreeksen zijn bytes, dus is er geen binaire modus om naar te zoeken - de :raw-layer is het hele verhaal. Raw openen, lezen, coderen, schrijven:

use MIME::Base64 qw(encode_base64);
open my $in, "<:raw", $ARGV[0] or die $!;
local $/;
my $bytes = <$in>;
close $in;
open my $out, ">:raw", $ARGV[1] or die $!;
print {$out} encode_base64($bytes);
close $out;

De :raw-layers tellen. Zonder ze zou Perl de bytes als platform-tekst willen interpreteren bij het binnen- en buitengaan, en op een systeem met een andere standaardcodering is dat precies de corruptie die je niet ziet totdat het bestand ergens anders wordt geopend. En onthoud de groottefactuur als je opslag plant: een afbeelding van 500 KB wordt een tekstbestand van 670 KB, en een video van 1 GB wordt 1,33 GB.

Voor bestanden die te groot zijn om in het geheugen te houden, geeft de documentatie van de module zelf je de regel: codeer in blokken die een veelvoud zijn van 57 bytes, want 57 bytes data vult exact één regel van 76 tekens, 76 zijnde 57 keer 4 gedeeld door 3. Blok op die grens en je krijgt nooit vultekens in het midden van de stroom:

use MIME::Base64 qw(encode_base64);
open my $in, "<:raw", $ARGV[0] or die $!;
while (read($in, my $buf, 57 * 10)) {
  print encode_base64($buf);
}
close $in;

Elk blok landt exact op regellimieten, het laatste, mogelijk korte, blok draagt de uiteindelijke vultekens, en het resultaat is byte voor byte hetzelfde als het hele bestand slurpen en het in één keer coderen, alleen met een constante geheugenvoetafdruk. En als je helemaal geen script nodig hebt, dekt de one-liner het, met -0777 dat de invoer slurpt en het lege tekenreeks-argument dat de uitvoer op één regel houdt:

perl -MMIME::Base64 -0777 -ne 'print encode_base64($_, "")' < file > file.b64

E-mail: MIME-lichamen en bijlagen

E-mail is waar Base64 zijn naam verdiende. De MIME-standaard zegt dat data die niet veilig als rauwe tekst kan reizen, moet worden verzonden met Content-Transfer-Encoding: base64, in regels van maximaal 76 tekens. Als je met MIME::Lite mail bouwt, is het hele ding één argument, en doet de module het coderen, het omwikkelwerk en de kop voor je:

use MIME::Lite;
my $mime = MIME::Lite->new(
  From    => 'me@example.com',
  To      => 'you@example.com',
  Subject => 'A file',
  Type    => 'text/plain',
  Data    => 'The body text.',
);
$mime->attach(
  Type     => 'application/octet-stream',
  Data     => $bytes,
  Encoding => 'base64',
  Filename => 'hello.txt',
);

Het Encoding-argument is de trigger: MIME::Lite base64-codeert de bijlage in regels van 76 tekens (met het standaard kale regeleinde van de module; de e-mailtransportlaag maakt er CRLF van) en stempelt het deel met de bijpassende Content-Transfer-Encoding-kop. Email::MIME kiest dezelfde houding en base64-codeert elke bijlage die je hem geeft als rauwe datatekenreeks (zijn documentatie: "alle delen die op deze manier worden aangemaakt, worden met base64 gecodeerd, om op de veilige kant te spelen"). Als je een rauw MIME-bericht met de hand samenstelt, is het equivalent de twee regels uit de omwikkelsectie, encode_base64($bytes, "\r\n") plus de kopregel, en dat is het hele protocolkant-verhaal.

Databases, config en omgevingsvariabelen

Databases: binaire data reist vaak als Base64 in een TEXT-kolom, omdat de kolom niet kan beloven willekeurige bytes onaangetast door te laten. Sla de één-regel-vorm op, nooit de omwikkelde vorm, of je volgende SELECT geeft een tekenreeks terug met regeleinden in het midden van de waarde:

use MIME::Base64 qw(encode_base64);
# $dbh is een al verbonden DBI-handle
my $stmt = $dbh->prepare(q{UPDATE photos SET data = ? WHERE id = ?});
$stmt->execute(encode_base64($bytes, ""), 42);

Configbestanden hebben dezelfde vorm: een JSON-document waar het binaire of geheime veld een één-regel-Base64-tekenreeks is, en precies daarom bestaat het tweede argument:

use JSON::PP qw(encode_json);
my $config = {
  api_key  => encode_base64($key_bytes, ""),
  logo_png => encode_base64($png_bytes, ""),
};
open my $fh, ">:raw", "app.json" or die $!;
print {$fh} encode_json($config);
close $fh;

Omgevingsvariabelen verdienen een woord van waarschuwing. Base64 is prima voor kleine tokens in de omgeving, maar de gecodeerde vorm is 33 procent groter dan het origineel, en het besturingssysteem plaatst een plafond op elk argument. Op Linux is de limiet 128 KB per tekenreeks, doorgevoerd door execve, en een grote blob in een omgevingsvariabele faalt niet beleefd: het kindproces crasht met een cryptische fout op het moment dat het wordt gegenereerd. Kleine waarden in de omgeving, grote waarden in een bestand of een database.

Prestatie: C, niet Perl, doet het werk

De core-module is geïmplementeerd in C, en die C stamt af van code die in 1991 voor metamail werd geschreven, en dat is een grappig feit totdat je de implicatie merkt: de encoder heeft drie decennia aan tuning gehad. Op een moderne machine verwerkt het data in een tempo van gigabytes per seconde, en dat is sneller dan de schijf of het netwerk waar het meestal in voedt, dus is Base64 zelf bijna nooit de flesnek. De I/O wel.

Voor het zeldzame systeem zonder C-compiler biedt de zuiver-Perl-tweeling MIME::Base64::Perl op CPAN dezelfde basisinterface, een paar keer langzamer maar nog steeds comfortabel voor gewone werkbelastingen. En twee gewoontes houden de grote klussen voorspelbaar: stream in 57-byte-blokken in plaats van slurpen, en meet je buffers vooraf af met encoded_base64_length vóór je alloceert, en dat spaart je zowel het gokwerk als de herallocatie.

Valkuilen, gerangschikt op middagkost

De vallen, in ruwweg de volgorde waarin ze bijten:

Val Wat er gebeurt Oplossing
Het tweede argument vergeten de uitvoer arriveert omwikkeld op 76 tekens met een afsluitend regeleinde, en je URL, JSON-veld of kop breekt in het midden van de waarde geef "" voor één-regel-uitvoer, en behoud de omwikkeling voor bestemmingen die hem verwachten
Omwikkeling op de verkeerde breedte een PEM-afnemer verwacht regels van 64 tekens en krijgt 76, of een MIME-lichaam overschrijdt de 76-teken-limiet pas de breedte aan het dialect aan: "" voor geen, "\r\n" voor MIME, een hulpfunctie voor PEM
De breed-teken-croak een tekenreeks met codes boven 255 crasht met Breed teken in subrutine-ingang halverwege het verzoek stuur de karakters eerst door Encode, bewust benoemd, vóór de encode_base64-aanroep
Dubbele codering al-UTF-8-bytes opnieuw door encode("UTF-8", ...) sturen maakt van Hëllo Hëllo bytes worden exact één keer gecodeerd; check utf8::is_utf8() als je twijfelt
Alfabetten stilletjes mengen een waarde gecodeerd met - en _ raakt een standaardalfabet-decoder en komt terug als rommel één dialect per waarde, van begin tot eind: kies base64url of standaard op het grensvlak
Het afsluitende regeleinde encode_base64 voegt het regeleinde toe, ook als de laatste regel exact vol is, en een straffe afnemer ziet een lege regel chomp of rtrim het resultaat als de afnemer kieskeurig is
Omwikkelde waarden in een database regeleinden landen in een TEXT-kolom en de volgende SELECT geeft een kapot token terug sla de één-regel-vorm op; wikkel pas om aan de bestemming
Omgevingsvariabelen met grote blobs de groei van 33 procent plus de per-argument-limiet van het besturingssysteem doodt het kindproces bij het ontstaan met een cryptische fout kleine waarden in de omgeving, grote waarden in een bestand of een database
Aannemen dat Base64 bescherming is het formaat verbergt niets, en de openbare verslaggeving documenteert echte voorvallen waarin een gebruiker een IMAP-uitwisseling plakte en per ongeluk een wachtwoord onthulde behandel de uitvoer als vertrouwelijk vanaf het moment dat hij wordt geproduceerd, en houd hem uit logs
Plannen zonder de groottefactuur een afbeelding van 500 KB wordt 670 KB tekst, en de opslag- of payload-limiet die je niet hebt gecheckt, bijt rekken op 4/3 van de oorspronkelijke grootte in vóórdat je je verbindt

Een geschiedenis, verteld door de encoder

De Base64-encoder van Perl heeft een loopbaan die een minuut waard is, en die begint in het eerste webtoolkit:

  • Geboren in libwww perl. De encoder begon zijn leven als LWP::Base64, geschreven door Martijn Koster en Joerg Reichelt, en dat nam Gisle Aas op in libwww perl als MIME::Base64; het promoveerde in april 1997 naar een eigen CPAN-distributie, versie 2.00, met een changelog-vermelding die simpelweg zegt dat het is gebaseerd op libwww perl 5.08.
  • Het snelheidstijdperk. Versie 2.07 in 1998 leverde een snellere en slimmere C-implementatie van de decoder, ongeveer 25 procent sneller op de destijds moderne Linux-kasten, en de tuning ging nog een decennium door.
  • Het Unicode-tijdperk. Perl 5.8 in 2002 bracht karakters met codes boven 255 in gewone tekenreeksen, en de module antwoordde in stappen: 2.12 in 2001 degradeerde UTF-8-tekenreeksen vóór het coderen, en de moderne Breed teken in subrutine-ingang-croak is de manier waarop de encoder zich daaraan vasthoudt. De 2.13-sync met de core in datzelfde jaar bracht EBCDIC-ondersteuning mee, een herinnering dat Base64 in Perl nog steeds op mainframes draait.
  • Het commandoregel-tijdperk. Releases van 2.14 in 2003 tot 3.05 in 2004 bundelden een écht encode-base64-commando, samen met zijn decode- en quoted-printable-tweelingen; 3.06 in 2005 verplaatste de scripts naar een aparte MIME Base64 Scripts-distributie.
  • De URL-veilige aankomst. RFC 4648 standaardiseerde het URL-veilige alfabet in 2006, een losse MIME::Base64::URLSafe-module verscheen datzelfde jaar, en de core-module haalde in 3.11 in 2010 in met encode_base64url in een enkele aanroep.
  • De moderne reeks. Versie 3.16 in 2020 herbouwde de verpakking en verhoogde de ondergrens naar Perl 5.6; de huidige core-Perls leveren de 3.16-reeks mee, en de module wordt onderhouden binnen de core-distributie, en dat is zo veilig een thuis als een core-module maar kan hebben.

Grappige feiten, specifiek over Perl

De weetjes die dit verhaal een goed verhaal maken:

  • Het voorbeeld in de POD is een magische zin. Sinds 1997 codeert de eigen documentatie van de module Aladdin:open sesame, en dus is de tekenreeks QWxhZGRpbjpvcGVuIHNlc2FtZQ== al bijna dertig jaar het visitekaartje van de module.
  • Het standaard regeleinde is het ene dat je waarschijnlijk niet verwachtte. Het is een gewone \n, niet het CRLF dat MIME spreekt. De eigen conventie van de RFC heeft het tweede argument nodig, en de module komt met het standaard van de programmator, niet dat van het protocol.
  • De lege tekenreeks heeft een speciale regel. Codeer niets en je krijgt niets terug, geen nieuwe regel toegevoegd: het ene gedocumenteerde uitzondering op de afsluitende-regeleinde-regel, en de reden waarom een leeg bestand schoon heen en weer gaat.
  • De IMAP-neef heeft een komma. De mailboxnaam-variant uit RFC 3501 ruilt de / in het alfabet in voor een komma, dus kan een Base64-tekenreeks van een IMAP-server een letter bevatten die de standaarddecoder als ruis behandelt.
  • De afstamming uit 1991 is echt. De C-implementatie stamt af van metamail, het mailprogramma van Bellcore uit 1991, drie jaar vóór de geboorte van Perl 5, en dus is elke encode_base64-aanroep deels jaren-negentig-code.
  • De zuiver-Perl-tweeling heeft haar eigen verhaal. Toen versie 3.00 in 2004 de zuiver-Perl-implementaties uit de core-module haalde, noemde de changelog ze het vet dat echte problemen in de XS-implementaties verbergt, en ze werden opnieuw uitgebracht als MIME::Base64::Perl, waar ze nog steeds wonen.

Dus de volgende keer dat ruwe bytes moeten reizen door een wereld van alleen maar tekst, ken je het hele verhaal. Eén functieaanroep doet het werk, het verborgen regeleinde is een besluit dat je met het tweede argument neemt, de breed-teken-croak is de manier waarop de encoder je Unicode eerlijk houdt, het URL-veilige dialect is al sinds 2010 één aanroep, bestanden streamen in 57-byte-blokken, en de factuur van 33 procent is de entreeprijs. En als je ooit de reis in de andere richting moet maken, een tekenreeks van letters pakken en de oorspronkelijke bytes eruit halen, dan dekt het gerelateerde artikel over Base64-decodering in Perl, hieronder gelinkt, dat ritueel in dezelfde diepte.

Laatst bijgewerkt: 2026-10-06

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