Einen String kodieren und dekodieren
use strict;
use warnings;
use utf8;
use MIME::Base64 qw(encode_base64 decode_base64);
use Encode qw(encode decode);
my $enc = encode_base64(encode('UTF-8', 'héllo wörld'), '');
print "$enc\n"; # aMOpbGxvIHfDtnJsZA==
print decode('UTF-8', decode_base64($enc)), "\n"; # héllo wörld
Perl unterscheidet Zeichenstrings von Bytestrings, und encode_base64 will Bytes, Text geht also zuerst durch Encode::encode('UTF-8', ...). Ein String mit Zeichen über U+00FF bricht mit Wide character in subroutine entry ab; einer mit ausschließlich Latin-1-Zeichen wie é wird stillschweigend als Latin-1 statt als UTF-8 kodiert.
Das Argument '' erklärt der nächste Abschnitt.
Das Zeilenende-Argument
encode_base64($bytes, $eol) bricht die Ausgabe nach 76 Zeichen um und hängt $eol an jede Zeile an, auch an die letzte. Der Standard ist "\n":
print encode_base64('hi'); # "aGk=\n"
print encode_base64('x' x 60);
# eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4
# eHh4
Für MIME-E-Mail-Inhalte ist das korrekt, für fast alles andere falsch: HTTP-Header, JSON, URLs und Datenbankfelder. Übergeben Sie '' für eine einzeilige Ausgabe oder "\r\n" für Zeilenenden streng nach RFC 2045.
Für einen HTTP-Header bedeutet das:
print "Authorization: Basic ", encode_base64('user:pass', ''), "\n";
# Authorization: Basic dXNlcjpwYXNz
Beim Dekodieren spielen Zeilenenden keine Rolle: decode_base64("aGVs\r\nbG8K") liefert "hello\n", MIME-Inhalte mit \n oder \r\n lassen sich also direkt dekodieren.
URL-sicheres Base64
MIME::Base64 exportiert auf Anfrage encode_base64url und decode_base64url. Der Encoder lässt das Padding weg, und der Decoder akzeptiert Eingaben ohne Padding:
use MIME::Base64 qw(encode_base64url decode_base64url);
print encode_base64url('<<???>>'), "\n"; # PDw_Pz8-Pg
print decode_base64url('PDw_Pz8-Pg'), "\n"; # <<???>>
Für URL-sichere Ausgabe mit Padding übersetzen Sie die Standardform mit tr:
my $padded = encode_base64('<<???>>', '') =~ tr{+/}{-_}r; # PDw_Pz8-Pg==
Das Flag /r (Perl 5.14+) liefert die geänderte Kopie zurück. Base64 vs. Base64URL erklärt, welche Systeme welche Variante erwarten.
Eine Datei kodieren und dekodieren
Öffnen Sie Dateien im Modus :raw, damit keine Schicht Zeilenumbrüche oder Kodierungen umwandelt:
open my $in, '<:raw', 'hello.txt' or die $!;
my $data = do { local $/; <$in> };
close $in;
print encode_base64($data, ''), "\n"; # aGVsbG8K
open my $out, '>:raw', 'copy.txt' or die $!;
print {$out} decode_base64('aGVsbG8K');
close $out;
Große Dateien in Blöcken
Lesen Sie in Vielfachen von 57 Bytes. 57 Bytes ergeben genau 76 Zeichen, jeder Block erzeugt also vollständige MIME-Zeilen, und die aneinandergehängte Ausgabe ist identisch mit der Kodierung der ganzen Datei:
open my $in, '<:raw', 'big.bin' or die $!;
open my $out, '>:raw', 'big.b64' or die $!;
while (read($in, my $buf, 57 * 1024)) {
print {$out} encode_base64($buf);
}
close $in;
close $out;
Das Ergebnis ließ sich mit base64 -d wieder in die ursprüngliche 300-KB-Datei dekodieren. Für einzeilige Ausgabe verwenden Sie encode_base64($buf, '') mit einer beliebigen durch 3 teilbaren Blockgröße. Das Modul kann Größen auch ohne Kodieren berechnen: MIME::Base64::encoded_base64_length('hello') liefert 9 (einschließlich Zeilenumbruch) und decoded_base64_length('aGVsbG8=') liefert 5. Die Formel steht in der Anleitung zum Größenzuwachs.
Für die Kommandozeile gibt es zwei Einzeiler, die man kennen sollte:
perl -MMIME::Base64 -0777 -ne 'print encode_base64($_, "")' < file.bin
perl -MMIME::Base64 -0777 -ne 'print decode_base64($_)' < file.b64 > file.bin
Validierung: decode_base64 beschwert sich nie
decode_base64 ist absichtlich tolerant. Es verwirft alles außerhalb des Alphabets, hört beim Padding auf und liefert, was es hat:
decode_base64('aGk=!!'); # "hi"
decode_base64('aGk'); # "hi" (missing padding is fine)
decode_base64('aGl='); # "hi" (non-zero padding bits ignored)
decode_base64('a'); # "" (too short, no warning)
decode_base64('PDw_Pz8-Pg=='); # "<<\x0f\xcf\xc3\xe0", not "<<???>>"
Der letzte Fall ist der gefährliche: URL-sichere Eingaben im Standard-Decoder erzeugen stillschweigend falsche Bytes. Verwenden Sie für Tokens decode_base64url. Bei nicht vertrauenswürdigen Eingaben prüfen Sie Alphabet und Länge und bestätigen dann den Roundtrip:
sub is_base64 {
my ($s) = @_;
return 0 unless $s =~ m{\A[A-Za-z0-9+/]*={0,2}\z} && length($s) % 4 == 0;
return encode_base64(decode_base64($s), '') eq $s ? 1 : 0;
}
# is_base64('aGk=') = 1, 'aGk' = 0, 'aGl=' = 0, 'aGk=!!' = 0
Der Base64-Validator führt dieselben Prüfungen im Browser aus und zeigt, welches Zeichen falsch ist. Für kaputte Strings aus Logs probieren Sie Base64 reparieren; Häufige Base64-Fehler erklärt, was sie meist verursacht.
Fallstricke
- Abschließender Zeilenumbruch in Headern.
"Authorization: Basic " . encode_base64("$user:$pass")endet mit\n, was die Anfrage kaputt macht. Übergeben Sie''. - Doppelte Kodierung. Mit
use utf8und einer Ausgabeschicht:encoding(UTF-8)zeigt die Ausgabe dekodierter Bytes ohnedecode('UTF-8', ...)Zeichensalat wiehéllo. - Base64 für geheim halten. Jeder kann es umkehren. Siehe Kodierung vs. Verschlüsselung.
Häufige Fragen
Warum fügt encode_base64 einen Zeilenumbruch an?
Das zweite Argument ist das Zeilenende, und sein Standardwert ist "\n". Die Ausgabe wird nach 76 Zeichen umgebrochen und endet immer mit diesem String. Übergeben Sie '', um eine einzige Zeile zu erhalten.
Was bedeutet „Wide character in subroutine entry“?
Sie haben einen Zeichenstring mit Codepoints über 255 an encode_base64 übergeben, das nur Bytes akzeptiert. Kodieren Sie den Text zuerst: encode_base64(encode('UTF-8', $text), '').
Prüft decode_base64 seine Eingabe?
Nein. Es ignoriert Zeichen außerhalb des Alphabets und liefert, was es dekodieren kann, decode_base64('aGk=!!') ergibt also hi. Prüfen Sie den String vor dem Dekodieren mit einem regulären Ausdruck, wenn die Eingabe nicht vertrauenswürdig ist.