Haben Sie mit dem Base64-Format zu tun? Dann ist diese Website genau das Richtige für Sie! Nutzen Sie unser superpraktisches Online-Tool, um Ihre Daten zu kodieren oder zu dekodieren.

Base64-Dekodierung in Ruby: Ein vollständiger Leitfaden

Irgendwo zwischen Ihnen und den ursprünglichen Daten steht eine Mauer aus Zeichen: Groß- und Kleinbuchstaben, Ziffern, vielleicht ein Plus, ein Schrägstrich oder ein Bindestrich, und vielleicht noch ein Gleichheitszeichen, das am Ende parkt. Ihr Editor weiß nicht, was das für ein Dateityp ist. Ihre Datenbank stopfte es in eine Textspalte. Es ist in einem HTTP-Header, in einer URL, in einem YAML-Schlüssel oder in einem Support-Ticket mit .b64-Anhang angekommen. Sie erkennen es im Handumdrehen - Base64 - und jetzt brauchen Sie die Bytes zurück. In Ruby ist dieser Wunsch nur ein require-Statement und ein Methodenaufruf entfernt.

Falls Ihnen das Format noch neu ist, hier die Dreißig-Sekunden-Version. Base64 schreibt rohe Daten drei Bytes zur Um: Jede Gruppe von drei Bytes wird zu vier Zeichen aus einem 64-Zeichen-Alphabet, und wenn die Eingabe nicht ohne Rest durch drei teilbar ist, werden ein oder zwei =-Zeichen als Padding angehängt, damit die Ausgabe immer auf ein Vielfaches von vier landet. Dekodieren ist die Rückreise - vier Zeichen hinein, drei Bytes heraus - deshalb ist das Ergebnis immer kleiner als die Eingabe, ungefähr drei Viertel der Größe. Die Startseite dieser Site arbeitet jeden Winkel des Formats im Detail durch, also verbringt dieser Leitfaden seine Energie genau dort, wo sie hingehört: auf der Ruby-Seite des Jobs.

Die gute Nachricht: Jede Ruby-Installation bringt das komplette Dekodier-Toolkit mit. Das Base64-Modul braucht nichts Installiertes, und seine drei Dekodierer sind so klein, dass Sie ihren gesamten Quellcode in einem einzigen Rutsch lesen können. Die Warnung: Der Dekodierer, zu dem Sie zuerst greifen, ist auch der, der nie murr - eine wunderschöne Eigenschaft für E-Mails und eine schreckliche für die Sicherheit. Am Ende dieses Leitfadens wissen Sie genau, was jeder Dekodierer akzeptiert, wie Sie die Bytes, die er Ihnen zurückgibt, in Text verwandeln, den Ruby Ihnen zum Gebrauch erlaubt, und was Sie mit jedem Payload tun, den ein Ruby-Entwickler tatsächlich dekodiert - JWTs, Auth-Header, Data-URIs, E-Mail-Body, PEM-Rüstung, Dateien, Konfig-Blobs und Riesige.

Lernen Sie das Toolkit kennen

Alles beginnt mit einem require. Es gibt keinen Installationsschritt, keine Plattform-Spezialitäten, keine native Erweiterung, die gebaut werden müsste:

require "base64"
puts Base64::VERSION
# => 0.2.0 zum Beispiel auf einer Standard-Ruby-3.3

Hier ist die komplette Dekodier-Seite des Toolkits in einer Tabelle, in der Reihenfolge, wie oft Sie zu jeder Methode greifen werden:

Dekodierer Umgang mit Fremdzeichen Padding-Regeln Wenn etwas schiefgeht
Base64.decode64(str) ignoriert alles, was nicht im Standard-Alphabet ist, einschließlich Zeilenumbrüche und Leerzeichen ganz egal, sogar falsches Padding nichts - es wirft nie etwas, es gibt einfach zurück, was es dekodieren konnte
Base64.strict_decode64(str) lehnt jedes Zeichen außerhalb des Standard-Alphabets ab muss vorhanden sein und exakt richtig wirft ArgumentError
Base64.urlsafe_decode64(str) akzeptiert das URL-sichere Alphabet und das Standard-Alphabet, lehnt alles andere ab optional, aber wenn vorhanden, muss es korrekt sein wirft ArgumentError

Wenn Sie wissen möchten, was Ihre Werkzeuge unter der Haube tun: Die gesamte Dekodier-Seite des Moduls ist ein dünner Wrapper um zwei Templates der pack/unpack-Maschinerie aus dem Kern, die in C im Ruby-Kern implementiert ist:

# die gesamte Dekodier-Seite des Moduls, verdichtet
def decode64(str)
  str.unpack1("m")
end
def strict_decode64(str)
  str.unpack1("m0")
end

Das m-Template ist der nachsichtige Leser, m0 der strenge, und dieser Unterschied eines einzigen Zeichens erklärt die gesamte Persönlichkeitslücke zwischen den ersten beiden Dekodierern. Weil die Schwerstarbeit mit Kern-Geschwindigkeit abläuft, bleibt das Modul reines Ruby und kaut trotzdem Megabytes in einstelligen Millisekunden weg.

decode64: Das Chamäleon

Base64.decode64 ist der Dekodierer, der zu allem Ja sagt. Füttern Sie ihn mit einem sauberen Payload, und er dekodiert ihn. Füttern Sie ihn mit einem MIME-artigen Blob voller Zeilenumbrüche, und er zuckt die Schultern. Füttern Sie ihn mit einem String, der überhaupt kein Base64 ist, und er gibt Ihnen zurück, was er gerade so herausquetschen konnte, ohne auch nur eine einzige Warnung:

require "base64"
Base64.decode64("aGVsbG8gd29ybGQ=")
# => "hello world"
Base64.decode64("Zm9vCmJh\ncgptYW4=\n")
# => "foo\nbar\nman"

Diese zweite Zeile ist die gesamte Persönlichkeit in einem einzigen Beispiel. Der Dekodierer überspringt alles, was nicht Teil des Standard-Alphabets ist - Zeilenumbrüche, Leerzeichen, das ein oder andere Steuerzeichen - und dekodiert den Rest. Genau so soll MIME-Base64 sich verhalten, deshalb ist decode64 das richtige Werkzeug für alles, was durch E-Mail gereist ist.

Die Kehrseite ist es, was ihn gefährlich macht. Weil der Dekodierer nie murr, sagt er Ihnen auch nie, wenn die Eingabe falsch war:

Base64.decode64("not base64 at all!")
# => zehn Bytes völlig glaubwürdig aussehender Müll
Base64.decode64("====")
# => ""

Das erste Beispiel findet die Zeichen, die zufällig gültige Alphabetbuchstaben sind, dekodiert sie und reicht Ihnen Bytes, die Sie beinahe ungeprüft direkt in eine Datei schreiben würden. Das zweite Beispiel gibt für einen String aus vier Padding-Zeichen einen leeren String zurück. Nichts wird geworfen, nichts wird protokolliert. Wenn Ihre Eingabe nicht vertrauenswürdig ist, ist dieses Schweigen eine Funktion, die Sie abschalten wollen - genau dafür sind die nächsten beiden Dekodierer da.

Noch eine Eigenart, die es zu wissen gilt, denn genau so etwas versteckt sich monatelang in der Produktion: Das Dekodieren stoppt bei dem ersten =-Zeichen. Alles nach dem Padding ist kein Fehler; es wird einfach nie gelesen:

Base64.decode64("aGVsbG8=Zm9vYmFy")
# => "hello"   der "Zm9vYmFy"-Teil ist für den Dekodierer unsichtbar

strict_decode64: Der Türsteher

Base64.strict_decode64 ist der Dekodierer mit der Kontrollliste. Er akzeptiert nur das Standard-Alphabet (A bis Z, a bis z, 0 bis 9, Plus, Schrägstrich), er verlangt, dass jedes Padding exakt richtig ist, und er verweigert ein einziges Byte, wenn auch nur eine Regel verletzt wird:

Base64.strict_decode64("aGVsbG8gd29ybGQ=")
# => "hello world"
Base64.strict_decode64("aGVsbG8gd29ybGQ")
# => wirft ArgumentError
Base64.strict_decode64("Zm9vCmJh\ncgptYW4=")
# => wirft ArgumentError

Die letzte Zeile ist die aufschlussreiche: derselbe Payload, den decode64 fröhlich dekodierte, wirft jetzt wegen eines einzigen Zeilenumbruchs. Fehlendes Padding, überflüssiges Padding, ein Bindestrich, ein Unterstrich, ein Leerzeichen - all das ist ein Vergehen, und der ganze Payload geht mit unter:

begin
  Base64.strict_decode64("aGVsbG8")
rescue ArgumentError => e
  puts e.message
end
# => invalid base64

Der Türsteher überwacht sogar Ecken des Formats, an die Sie nicht denken würden. Wenn ein Base64-String mit Padding endet, werden einige Bits im letzten Zeichen nie verwendet, und der RFC sagt, dass ein konformer Encoder diese Bits auf null setzen muss. Ruby prüft das:

Base64.strict_decode64("QQ==")
# => "A"
Base64.strict_decode64("QR==")
# => wirft ArgumentError (die Padding-Bits sind nicht null)

Der zweite String würde zu exakt demselben Byte dekodiert wie der erste, wenn der Dekodierer schlampig wäre. Ruby ist es nicht. In der Praxis macht das strict_decode64 zur richtigen Standardeinstellung für jede Eingabe, die Sie nicht selbst kodiert haben: Es verwandelt Tippfehler, Abbrüche und das falsche Alphabet in laute, abfängbare Fehler statt in stille Datenkorruption.

urlsafe_decode64: Der Diplomat

Base64.urlsafe_decode64 gibt es für Payloads, die dorthin reisen, wo + und / reservierte Wörter sind: URLs, Tokens, Datenbankanreicher. Intern übersetzt es das URL-sichere Alphabet (Bindestrich und Unterstrich) zurück in das Standard-Alphabet, normalisiert das Padding und übergibt das Ergebnis an den strengen Dekodierer:

Base64.urlsafe_decode64("SGVsbG8gd29ybGQ")
# => "Hello world"
Base64.urlsafe_decode64("SGVsbG8gd29ybGQ==")
# => wirft ArgumentError (fünfzehn Zeichen brauchen ein Padding-Zeichen, nicht zwei)

Das erste Beispiel zeigt seine nützlichste Eigenschaft: Eingabe ohne Padding ist in Ordnung. Wenn der String kein Padding hat und seine Länge kein Vielfaches von vier ist, hängt der Dekodierer die fehlenden =-Zeichen einfach für Sie an - genau das, was JSON Web Tokens, der größte Verbraucher von URL-sicherem Base64, erzeugen. Wenn aber Padding vorhanden ist, muss es korrekt sein, genau wie beim strengen Dekodierer.

Es gibt eine Eigenart, über die die Dokumentation nicht laut ruft: Der Diplomat spricht beide Sprachen. Weil die Methode Bindestriche und Unterstriche vor dem strengen Dekodieren umschreibt, akzeptiert sie auch Strings aus dem Standard-Alphabet:

Base64.urlsafe_decode64("aGVsbG8=")
# => "hello"   das Standard-Alphabet wird ebenfalls akzeptiert

Diese Nachsicht ist bequem, aber es bedeutet, dass Sie mit dieser Methode nicht feststellen können, aus welchem Alphabet ein Payload stammt. Wenn Ihnen das wichtig ist, prüfen Sie die Zeichen selbst, bevor Sie dekodieren.

Und anders als decode64 kennt der Diplomat keine Gnade für Leerzeichen und Umbrüche. Ein Zeilenumbruch irgendwo in einem URL-sicheren Payload wirft ArgumentError, also streichen Sie die Zeilenumbrüche zuerst, wenn Ihre Eingabe aus einer umgebrochenen Datei kommt.

Bytes sind kein Text: Der Encoding-Schritt

Hier ist der Schritt, der selbst erfahrene Entwickler zum Stolpern bringt, weil Ruby ihn sichtbar macht. Ein dekodierter Base64-String ist immer mit der ASCII-8BIT-Kodierung (auch BINARY genannt) markiert, egal ob die ursprünglichen Daten ein PNG, ein JWT-Payload oder ein Liebesbrief in UTF-8 waren:

bin = Base64.decode64(Base64.strict_encode64("h\u{e9}llo"))
puts bin.encoding
# => ASCII-8BIT
puts bin.bytes
# => [104, 195, 169, 108, 108, 111]

Wenn der Payload binär ist - ein Bild, eine zip-Datei, ein Hash - behalten Sie ihn genau so und schreiben Sie ihn mit File.binwrite. Keine Konvertierung, keine Fragen. Wenn der Payload Text ist, sind die Bytes mit nahezu sicher UTF-8, und Sie müssen Ruby das sagen:

text = Base64.decode64(payload)
text.force_encoding("UTF-8")
if text.valid_encoding?
  puts text
else
  puts "not valid UTF-8 after all"
end

Die beiden Aufrufe machen verschiedene Dinge. force_encoding beschriftet nur die Bytes neu; valid_encoding? prüft dann, ob sie echtes UTF-8 bilden. Führen Sie sie in dieser Reihenfolge aus, denn die Validierung eines BINARY-Strings zuerst hat nichts zu validieren. Und eine kleine Vergleichsfalle, die man fürs Leben merkt: Ruby betrachtet einen BINARY-String nur dann als gleich mit einem UTF-8-String, wenn beide reines ASCII sind - also beschriften Sie neu, bevor Sie dekodierten Text mit Ihrem Original vergleichen:

decoded = Base64.decode64("aMOpbGxv")
puts decoded == "h\u{e9}llo"
# => false   gleiche Bytes, unterschiedliche Etiketten
decoded.force_encoding("UTF-8")
puts decoded == "h\u{e9}llo"
# => true

JWTs: Ein Token lesen, ohne den Schlüssel

Ein JSON Web Token ist drei Base64-Strings, mit Punkten zusammengeheftet: Header, Payload, Signatur. Die ersten beiden sind URL-sicheres Base64 ohne Padding von JSON-Dokumenten, was bedeutet, dass ein Token von jedem lesbar ist, der ihn jemals sieht - einschließlich Ihnen, ganz ohne Bibliothek:

require "base64"
require "json"
token = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIn0.dW5zaWduZWQ"
header_part, payload_part = token.split(".")[0, 2]
JSON.parse(Base64.urlsafe_decode64(payload_part))
# => {"sub"=>"1234567890", "name"=>"Alice"}

Für echte Arbeit werden Sie das jwt-Gem verwenden, das sich um den Teil kümmert, der Sie tatsächlich schützt - die Signatur - und um die Validierung der Claims:

# Gemfile: gem "jwt"
require "jwt"
token = JWT.encode(
  { sub: "1234567890", name: "Alice", exp: Time.now.to_i + 3600 },
  "my-secret-key",
  "HS256"
)
payload, header = JWT.decode(token, "my-secret-key", true, algorithm: "HS256")
puts payload["name"]
# => Alice

Zwei Sicherheitshinweise gehören hierher, denn beide haben Menschen bereits echte Vorfälle gekostet. Erstens ist der Payload nicht verschlüsselt; ihn zu dekodieren ist Lesen, kein Knacken, und die Signatur ist der einzige Schutz - behandeln Sie einen dekodierten Payload also niemals als vertrauenswürdige Eingabe. Zweitens: Fixieren Sie den Algorithmus in JWT.decode genau wie gezeigt. Weglassen Sie ihn, und der Header des Tokens selbst entscheidet, wie es verifiziert wird - genau diese kleine Portion Flexibilität nutzen die berühmten JWT-Algorithmus-Verwechslungsangriffe aus.

Basic Auth: Das Passwort in voller Sicht versteckt

Der älteste Authentifizierungs-Header im Web ist Base64 selbst. HTTP Basic Auth sendet die Anmeldedaten kodiert als user:password nach dem Wort Basic - und der Header reist bei jeder Anfrage mit, taucht also in jedem Log auf, das Sie je debuggen. Ein solches zu dekodieren heißt: abstreifen und zerlegen:

require "base64"
header_value = "Basic YWxpY2U6czNjcjN0IQ=="
b64 = header_value.sub("Basic ", "")
decoded = Base64.decode64(b64)
user, password = decoded.split(":", 2)
puts user
# => alice
puts password
# => s3cr3t!

Die Grenze 2 im split ist wichtig: Ein Passwort darf legalerweise Doppelpunkte enthalten, und Sie wollen immer nur beim ersten trennen. Rubys eigene Standard-Bibliothek baut diesen Header in Net::HTTP umgekehrt auf und nutzt dabei das Kern-pack-Template direkt:

require "net/http"
request = Net::HTTP::Get.new("https://example.org/api")
request.basic_auth("alice", "s3cr3t!")
puts request["Authorization"]
# => Basic YWxpY2U6czNjcjN0IQ==

Und der Sicherheitshinweis, der ausgesprochen werden muss, auch wenn er offensichtlich ist: Base64 ist ein Übersetzer, kein Schloss. Basic Auth ist nur über HTTPS akzeptabel. Die Kodierung existiert, damit Anmeldedaten als druckbarer Text über die Leitung reisen können - nicht damit sie geheim sind.

Data-URIs: Das Bild, das keine Datei ist

Eine Data-URI versteckt eine ganze Datei in einer URL: ein Medientyp, das Wort base64, ein Komma und die kodierten Bytes. Browser rendern sie in img-Tags und CSS, und Single-File-HTML-Apps lieben sie, weil es keine zweite Anfrage zu stellen gibt. Eine in Ruby zu bauen dauert eine Zeile:

require "base64"
png = File.binread("logo.png")
data_uri = "data:image/png;base64,#{Base64.strict_encode64(png)}"

Dekodieren ist das Umgekehrte, mit zwei Details, über die man stolpert. Das Komma ist der Trenner, also genau einmal splitten, und der Medientyp-Teil kann alles sein - sogar nichts:

data_uri = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=="
media_part, b64 = data_uri.split(",", 2)
puts media_part
# => data:image/png;base64
bytes = Base64.strict_decode64(b64)
File.binwrite("restored.png", bytes)

Verwenden Sie hier strict_decode64, nicht decode64: Ein Data-URI-Payload ist eine einzige saubere Zeile, und Sie wollen einen lauten Fehler, wenn er korrupt ist. Bedenken Sie auch die Größensteuer - jedes Bild, das Sie inline einbetten, wächst um etwa ein Drittel - deshalb sind Data-URIs perfekt für Favicons, kleine Logos und Schriften, aber eine schlechte Idee für Hero-Fotos.

E-Mail: Zeilen zu 60 Zeichen und das mail-Gem

Base64 wurde für E-Mail erfunden, und die Narben sind zu sehen. SMTP wurde für kurze Zeilen aus 7-Bit-Text entworfen, deshalb bricht MIME-Base64 seine Ausgabe in kurze Zeilen um, und ein konformer Dekodierer muss die Zeilenumbrüche ignorieren. Rubys decode64 verhält sich genau so, also ist ein umgebrochener MIME-Body leichte Kost:

body = "Zm9vCmJh\ncgptYW4=\n"
Base64.decode64(body)
# => "foo\nbar\nman"

Das werden Sie selten von Hand schreiben. Das mail-Gem erledigt den gesamten MIME-Job für Sie: Anhänge werden automatisch Base64-kodiert, die Zeilen werden bei 60 Zeichen umgebrochen, bequem innerhalb der 76-Zeichen-Grenze von MIME, und die korrekten Header werden angehängt:

# Gemfile: gem "mail"
require "mail"
message = Mail.new do |m|
  m.from = "dev@example.org"
  m.to = "ops@example.org"
  m.subject = "Binary report"
  m.add_file("report.bin")
end
puts message.encoded
# der Anhang-Teil trägt Content-Transfer-Encoding: base64

Derselbe Trick versteckt sich in E-Mail-Headern. Eine nicht-ASCII-Betreffzeile kommt als RFC-2047-encoded word an: ein Charset, der Buchstabe B und Base64 zwischen Fragezeichen. Eines von Hand zu dekodieren ist eine kleine Übung in String-Chirurgie:

header_value = "=?UTF-8?B?w7wgc2VjcmV0cw==?="
charset, kind, b64 = header_value.sub(/\A=\?/, "").sub(/\?=$/, "").split("?")
text = Base64.decode64(b64).force_encoding(charset)
puts text
# => ü secrets

PEM: Schlüssel und Zertifikate in Rüstung

Schlüssel und Zertifikate verbringen den größten Teil ihres Lebens in PEM-Rüstung: eine BEGIN-Zeile, ein Block aus Base64 und eine END-Zeile. Die Rüstung stammt aus den 1980er-Jahren - Privacy-Enhanced Mail ist der Ursprung der gesamten Base64-Linie - aber sie ist immer noch das Format, das Ihre .crt- und .key-Dateien heute tragen.

Eine PEM-Datei von Hand zu dekodieren heißt einfach nur, die Rüstung abzuziehen und den nachsichtigen Dekodierer die Zeilenumbrüche kauen zu lassen:

require "base64"
pem = File.read("server.key")
body = pem.lines
  .reject { |line| line.start_with?("-----") || line.strip.empty? }
  .join
key_bytes = Base64.decode64(body)

Für den eigentlichen Gebrauch werden Sie den manuellen Schritt meist überspringen und den ganzen PEM-String an OpenSSL übergeben, das die Rüstung selbst liest:

require "openssl"
key = OpenSSL::PKey.read(File.read("server.key"))
puts key.class
# => OpenSSL::PKey::RSA, oder was auch immer der Schlüssel am Ende ist

Das einzige Interoperabilitäts-Detail, das es zu wissen lohnt: PEM-Zeilen sind klassisch 64 Zeichen lang, und der Dekodierer ignoriert Zeilenumbrüche ohnehin - also dekodiert ein 60-Zeichen-Wrapper oder eine einzige Riesenzeile genauso gut.

Dateien und die .b64-Konvention

Das häufigste Dateiformat in der Base64-Welt ist eine einfache Textdatei mit der Endung .b64 (oder manchmal .base64), die einen kodierten Payload enthält. Eine zu lesen ist eine dreistufige Hin-und-Rück-Reise:

require "base64"
encoded = File.read("payload.b64")
bytes = Base64.decode64(encoded)
File.binwrite("payload.bin", bytes)

Verwenden Sie auf dem Weg nach draußen File.binwrite - ein dekodiertes PNG oder eine zip ist binär, und Schreiben im Textmodus würde es auf Plattformen, die Zeilenumbrüche umschreiben, korrupt machen. Wenn Ihre .b64-Datei von einem Tool stammt, das Zeilen umbricht, kümmert sich decode64 gratis um die Zeilenumbrüche. Wenn Sie validieren statt tolerieren wollen, lesen Sie die Datei im Binärmodus ein und streichen Sie die Zeilenumbrüche vor einem strengen Dekodieren:

encoded = File.binread("payload.b64")
clean = encoded.delete("\r\n")
bytes = Base64.strict_decode64(clean)

Das binäre Lesen ist auf Windows wichtig, wo der Textmodus CRLF-Zeilenumbrüche als LF umschreibt - genau die Art von Mutation, die Sie nicht in einem String haben wollen, den Sie gleich validieren.

URL-sicheres Base64: Payloads, die in Links reisen

Das ist die Dekodierer-Perspektive auf die URL-sichere Variante, denn die Wahl, die Sie hier treffen, bestimmt, zu welchem der drei Dekodierer Sie greifen. URL-sicheres Base64 (RFC 4648, Abschnitt 5) tauscht die zwei Zeichen aus, die URLs nicht mögen - + wird zu -, / wird zu _ - und lässt meist auch das Padding weg. In Ruby treffen Sie es in Query-Parametern, Cookie-Werten, API-Identifiern, YouTube-artigen Video-IDs und natürlich in JWTs.

So verhalten sich die drei Dekodierer auf denselben Eingaben, denn genau dort, wo sie sich unterscheiden, entstehen Bugs:

Eingabe decode64 strict_decode64 urlsafe_decode64
aGVsbG8= (Standard, mit Padding) "hello" "hello" "hello"
aGVsbG8 (ohne Padding) "hello" ArgumentError "hello"
SGVsbG8gd29ybGQ- (Bindestrich in letzter Gruppe) "Hello world" (ein Byte zu kurz!) ArgumentError 12 Bytes, die korrekte Antwort
aGVsbG8=\n (anhängender Zeilenumbruch) "hello" ArgumentError ArgumentError
aGVs!bG8= (verirrtes Ausrufezeichen) "hello" ArgumentError ArgumentError

Zeile drei ist die Zeile, die Menschen beißt. Ein URL-sicherer Payload, der mit dem Standard-Dekodierer dekodiert wird, verliert still sein letztes Byte, statt etwas zu werfen, denn decode64 ignoriert den Bindestrich einfach. Wenn ein Payload aus einer URL stammen kann, dekodieren Sie ihn mit urlsafe_decode64.

Ein praktischer Hinweis: Falls Sie je einen URL-sicheren Payload in einen Kontext verlagern müssen, der nur das Standard-Alphabet versteht (eine Bibliothek, ein fremdes System), ist der klassische Interoperabilitäts-Trick - Alphabet übersetzen und das Padding selbst anhängen - drei Zeilen lang:

def standardize_urlsafe(b64)
  b64 = b64.tr("-_", "+/")
  b64 += "=" * ((4 - b64.length % 4) % 4)
  b64
end
Base64.strict_decode64(standardize_urlsafe("SGVsbG8gd29ybGQ"))
# => "Hello world"

Sie werden es selten brauchen - urlsafe_decode64 hängt das Padding bereits für Sie an - aber es ist das Muster, das Sie im Code anderer erkennen sollten, und das Muster, zu dem Sie greifen, wenn das Standard-Alphabet ist, was die andere Seite erwartet.

Konfiguration, Umgebungsvariablen und Datenbanken

Base64 taucht in der Konfiguration immer dann auf, wenn binäre Daten in einem Textdokument sitzen müssen. Eine .env-Datei, eine YAML-Konfiguration oder ein JSON-Einstellungs-Blob kann keine rohen Bytes sicher transportieren, also werden die Bytes kodiert, und irgendetwas in Ihrer Anwendung muss sie beim Start dekodieren:

require "base64"
b64 = ENV.fetch("APP_LOGO")
bytes = Base64.decode64(b64)
File.binwrite("logo.png", bytes)

YAML bekommt eine besondere Erwähnung, denn das Format hat ein natives Binary-Tag. Wenn Sie einen BINARY-String dumpen, schreibt Psych ihn als !binary-Skalar mit Base64 heraus, und beim Laden bekommen Sie Ihre Bytes unversehrt zurück - ganz ohne manuelle Kodierung:

require "yaml"
yaml_text = YAML.dump({ "logo" => File.binread("logo.png") })
puts yaml_text.lines.first(2)
# => "---"
# => "logo: !binary |-"
data = YAML.load(yaml_text)
puts data["logo"].encoding
# => ASCII-8BIT

In Datenbanken ist die Daumenregel: Wenn Ihre Datenbank einen echten Binärtyp hat, nutzen Sie ihn. Base64 in einer TEXT-Spalte ist das Muster, zu dem Sie greifen, wenn die Speicherungsschicht nur Strings spricht - manche Dokumenten-Stores, JSON-artige APIs oder ein Legacy-Schema, das Sie nicht ändern können - und der Preis ist die Ein-Drittel-Größensteuer auf der Spalte plus die Disziplin, beim Hineingehen zu dekodieren und beim Rausgehen an jeder Grenze neu zu kodieren.

Große Eingaben, stabiler Speicher

Das Modul ist pufferbasiert: Ein Dekodier-Aufruf liest den ganzen String auf einmal und gibt das ganze Ergebnis zurück. Es gibt keinen Streaming-Dekodierer in der Standard-Bibliothek, also ist der ehrliche Rat für große Payloads, den Speicher zu planen. Die gute Nachricht: Dekodieren macht die Dinge nur kleiner - die Ausgabe ist höchstens drei Viertel der Eingabe - also ist der Eingabe-String Ihre einzige große Allokierung.

Wenn ein Payload groß genug ist, dass er Sie beunruhigt, können Sie ihn in Vierer-Gruppen dekodieren, denn Base64-Gruppen zu viert sind in sich abgeschlossen und die letzte unvollständige Gruppe trägt ihr eigenes Padding:

require "base64"
def decode_in_chunks(b64)
  b64.scan(/.{1,4}/).reduce("") do |result, group|
    result + Base64.strict_decode64(group)
  end
end
restored = decode_in_chunks(Base64.strict_encode64("a" * 1_000_000))
puts restored.length
# => 1000000

Das funktioniert auf saubere, nicht umgebrochene Eingabe - dieselben Regeln, die strict_decode64 durchsetzt - denn eine einzelne Endgruppe ist nur mit vorhandenem Padding gültig. Für wirklich riesige Dateien, Multi-Gigabyte-Archive und dergleichen ist das Muster, die Datei in Scheiben zu lesen, jede Scheibe zu dekodieren und die Bytes auf die Festplatte zu streamen, damit immer nur eine Scheibe gleichzeitig im Speicher ist.

One-Liner für das Terminal

Zum Dekodieren in der Shell brauchen Sie keine Skript-Datei. Ruby kann das Modul bei Bedarf direkt laden:

ruby -rbase64 -e 'puts Base64.decode64(ARGV[0])' "aGVsbG8gd29ybGQ="
# => hello world

Und für Dateien übergeben Sie einen Dateipfad statt des Payloads selbst:

ruby -rbase64 -e 'print Base64.decode64(File.read(ARGV[0]))' payload.b64 > payload.bin

Zwei Stolpersteine wohnen hier. Erstens: Wenn Sie durch echo oder einen beliebigen Textbefehl pepend, reist ein anhängender Zeilenumbruch mit, und strict_decode64 wirft darauf - verwenden Sie decode64 oder wenden Sie chomp auf die Eingabe an:

echo "aGVsbG8gd29ybGQ=" | ruby -rbase64 -e 'print Base64.strict_decode64(STDIN.read.chomp)'

Zweitens: Behalten Sie print statt puts für binäre Ausgabe, denn puts hängt einen eigenen Zeilenumbruch an und würde das Ende Ihrer wiederhergestellten Datei korrupt machen.

Fallen, in die Ruby-Entwickler wirklich tappen

  • decode64 wirft nie etwas. Müll rein, Müll raus. Wenn Ihre Eingabe nicht vertrauenswürdig ist und Sie korrupte Bytes still akzeptieren, taucht der Bug Wochen später in einer korrupten Datei auf, nicht an der Dekodier-Zeile. Machen Sie einen strengen Dekodierer zur Standardeinstellung für alles, was Sie nicht selbst kodiert haben.
  • strict_decode64 und der anhängende Zeilenumbruch. Textdateien, echo-Pipes und Copy-Paste enden alle gerne mit einem Newline, und der strenge Dekodierer wirft darauf ArgumentError. Wenden Sie chomp zuerst auf die Eingabe an - oder lesen Sie sie im Binärmodus ein und löschen Sie die Zeilenumbrüche.
  • Der Encoding-Schritt wird vergessen. Ein dekodierter String ist BINARY, bis Sie etwas anderes sagen. Forcieren Sie UTF-8 (und prüfen Sie die Gültigkeit), bevor Sie das Ergebnis als Text behandeln, sonst bekommen Sie Mojibake und Encoding::CompatibilityError, in dem Moment, in dem Sie es mit UTF-8-Strings mischen.
  • BINARY mit UTF-8 vergleichen. Gleiche Bytes, unterschiedliche Etiketten, und == sagt false - es sei denn, der String ist zufällig reines ASCII. Beschriften Sie neu, bevor Sie vergleichen.
  • URL-sichere Eingabe durch den falschen Dekodierer. Bindestriche und Unterstriche werden von decode64 still verworfen, also kommt ein URL-sicherer Payload ein Byte zu kurz und korrupt zurück, ganz ohne Fehler. Verwenden Sie urlsafe_decode64.
  • Daten nach dem Padding sind unsichtbar. decode64 stoppt beim ersten =. Wunderbar für MIME, schrecklich, um einen Payload zu fangen, der abgeschnitten und dann von einem anderen Tool wieder mit Padding versehen wurde.
  • Nicht-kanonisches Padding wird still akzeptiert. Ein String wie QR== trägt Padding-Bits, die ein korrekter Encoder auf null gesetzt hätte; decode64 dekodiert ihn fröhlich, während strict_decode64 ihn ablehnt. Nichts wird Sie jemals darauf hinweisen, dass Ihr Encoder gelogen hat.
  • Dateilesungen im Textmodus auf Windows schreiben Zeilenumbrüche um, bevor Sie sie überhaupt sehen. Lesen Sie .b64-Dateien im Binärmodus ein, wenn Sie sie validieren wollen.

Gute Gewohnheiten für die Dekodier-Seite

  • Wählen Sie den Dekodierer nach der Quelle der Daten: strict_decode64 für alles Unvertraute (und ArgumentError als Ihre ungültige-Eingabe-Abzweigung rescuen), urlsafe_decode64 für Payloads, die in einer URL geboren wurden, und decode64 nur für Formate, die wirklich nachsichtig sind, wie MIME-Body.
  • Sobald Bytes dekodiert sind, entscheiden Sie ihre Identität: binär (ASCII-8BIT beibehalten, mit File.binwrite schreiben) oder Text (force_encoding auf UTF-8, dann valid_encoding? vor der Verwendung).
  • Niemals einfach nur dekodieren und vertrauen. Ein JWT-Payload ist lesbar, gerade weil er Base64 ist; die Signatur entscheidet, ob er echt ist. Ein Base64-String in einer Konfigurationsdatei ist Daten, kein Beweis.
  • Wenn Sie Validatoren schreiben, testen Sie sie gegen die langweiligen Fälle: der leere String, Eingabe ohne Padding, umgebrochene Eingabe, URL-sichere Eingabe und falsches Padding. Das sind die Fälle, die die drei Dekodierer voneinander trennen.

Eine kurze Geschichte von Base64 in Ruby

Das Base64-Modul ist seit über fünfzehn Jahren Teil von Rubys Standard-Bibliothek, und die Art, wie es ausgeliefert wird, hat sich stärker gewandelt, als Sie vielleicht erwarten:

  • 2008, Ruby 1.8.7: Das Modul erscheint mit encode64, decode64 plus zwei Methoden, die es nicht mehr gibt - b64encode (Umbruch bei einer gewählten Zeilenlänge) und decode_b (Dekodierung von RFC-2047-E-Mail-Headern). Alte Bücher und sogar einige alte Gems referenzieren sie immer noch, und heutzutage ist der Aufruf von beiden ein NoMethodError.
  • 2009, die 1.9-Linie: strict_encode64, strict_decode64, urlsafe_encode64 und urlsafe_decode64 kommen, und die zwei Legacy-Methoden werden in den Ruhestand versetzt (1.9.1 hatte beide Änderungen bereits im Januar 2009 ausgeliefert).
  • 2015, Ruby 2.3: urlsafe_encode64 bekommt das padding:-Keyword, damit Sie Ausgabe ohne Padding für Tokens und URLs erzeugen können.
  • 2020, Ruby 3.0: base64 wird aus der Standard-Bibliothek in ein eigenes Gem extrahiert, Version 0.1.0, im ruby/base64-Repository. Es erscheint als Default-Gem, daher funktioniert require "base64" einfach weiter.
  • 2023, Ruby 3.3: Version 0.2.0 fügt Base64::VERSION und ein deutlich reichhaltigeres Dokumentationspaket hinzu.
  • 2024, Ruby 3.4: Das Gem wird von einem Default-Gem zu einem Bundled-Gem umklassifiziert. Die praktische Konsequenz: In Bundler-basierten Projekten auf Ruby 3.4 und neuer listen Sie gem "base64" in Ihrem Gemfile (oder installieren Sie es mit gem install base64).
  • 2025, Ruby 4.0: Version 0.3.0 landet und bringt unter anderem RBS-Typsignaturen mit.

Durch all das hat sich eine Tatsache nie geändert: Das Modul ist ein paar Dutzend Zeilen reines Ruby, die auf den Kern-Templates pack und unpack sitzen. Keine C-Erweiterung, keine Abhängigkeiten, nichts zu bauen - und eine Downloadzahl im Hundert-Millionen-Bereich auf rubygems.org.

Ruby-Trivia für Neugierige

  • Die Dekodier-Seite des Moduls sind zwei einzeilige Methodenkörper, str.unpack1("m") und str.unpack1("m0"), plus die urlsafe-Variante, die ein Zeichen-Tausch und eine Padding-Korrektur auf dem strengen ist. Sie können das require löschen und es selbst schreiben.
  • Rubys eigene Net::HTTP verwendet nicht einmal das Base64-Modul für Basic Auth - es ruft das pack-Template direkt auf: ["user:pass"].pack("m0").
  • Rubys signierte und verschlüsselte Cookies sind unter der Haube Base64-Strings: ActiveSupports Message-Codec wählt für gewöhnliche Cookies strict_encode64 und für URL-sichere signierte IDs urlsafe_encode64 mit padding: false. Sie haben wahrscheinlich schon eines davon dekodiert, ohne es zu wissen.
  • Jede Digest-Klasse hat eine base64digest-Methode - Digest::SHA256.base64digest("hello") - ein One-Liner für Prüfsummen, die in Text leben müssen.
  • Rubys !binary-Tag in YAML ist Base64. Dumpen Sie einen BINARY-String mit Psych, und das Format erledigt die Kodierung still für Sie.
  • decode64 schert sich nicht darum, ob Ihre Zeilen 60, 64 oder 76 Zeichen lang sind oder eine einzige Riesenzeile. Das m-Template überspringt die Zeilenumbrüche, daher dekodieren umgebrochene und nicht umgebrochene Eingabe identisch.

Weiter geht's

Sie haben jetzt das komplette Dekodier-Toolkit: einen nachsichtigen Leser für MIME-geformte Blobs, einen strengen Türsteher für alles Unvertraute, einen URL-sicheren Diplomaten für Tokens und Links und den Encoding-Schritt, der die resultierenden Bytes in Text verwandelt, den Ruby Ihnen zum Gebrauch erlaubt. Die umgekehrte Richtung - zu entscheiden, in welchen der drei Encoder von Ruby Sie Ihre Bytes füttern, und das Alphabet, das Padding und die Zeilenumbrüche zu steuern - bringt ihre eigene Portion Überraschungen mit, angefangen bei einem anhängenden Newline, den niemand bestellt hat. Die andere Straßenseite wird ausführlich im Base64-Encoding-Artikel behandelt, verlinkt unten.

Zuletzt aktualisiert: 2026-09-08

Verwandter Artikel: Base64-Kodierung in Ruby: Ein vollständiger Leitfaden