Datei hochladen
  1. base64hub
  2. Base64 im Code
  3. Ruby
Code

Base64 in Ruby

Das Ruby-Modul Base64 ist eine dünne Hülle um Array#pack und String#unpack1. Die größte Falle: Die nicht strikten Methoden fügen Zeilenumbrüche ein. Die Ausgaben unten stammen von Ruby 3.4.

Einen String kodieren und dekodieren

Verwenden Sie die strict_-Methoden, sofern Sie nicht ausdrücklich MIME-Ausgabe wollen:

require "base64"

enc = Base64.strict_encode64("héllo wörld")
puts enc          # aMOpbGxvIHfDtnJsZA==

dec = Base64.strict_decode64(enc).force_encoding("UTF-8")
puts dec          # héllo wörld

Ruby-Strings tragen eine Kodierung, und dekodierte Strings kommen immer als BINARY (ASCII-8BIT) zurück, weil Base64 nicht weiß, was die Bytes bedeuten. force_encoding("UTF-8") etikettiert sie um, ohne zu kopieren. Lassen Sie es weg, scheitern Vergleiche mit UTF-8-Literalen, und String-Methoden behandeln jedes Byte als Zeichen.

Liegt der Quellstring in einer anderen Kodierung vor (etwa Windows-1252 aus einer Altdatei), rufen Sie vor dem Kodieren in Base64 .encode("UTF-8") auf, sonst sieht der Empfänger andere Bytes als erwartet.

encode64 vs. strict_encode64

encode64 ist die MIME-Variante. Sie fügt nach je 60 Zeichen einen Zeilenumbruch ein und endet immer mit einem:

Base64.encode64("hi")          # => "aGk=\n"
Base64.encode64("x" * 60)
# => "eHh4...eHh4\neHh4eHh4eHh4eHh4eHh4\n"   (60 chars per line)
Base64.strict_encode64("hi")   # => "aGk="

Dieses abschließende \n ist der häufigste Base64-Bug in Ruby: Es macht HTTP-Header, Signaturen und JSON-Vergleiche kaputt. Beachten Sie, dass Ruby nach 60 Zeichen umbricht, während RFC 2045 bis zu 76 erlaubt und die meisten anderen Sprachen 76 verwenden. Beides ist für E-Mail gültig; brauchen Sie genau 76, zeigt der MIME-Base64-Encoder dieses Format.

URL-sicheres Base64 mit und ohne Padding

Base64.urlsafe_encode64("<<???>>")                  # => "PDw_Pz8-Pg=="
Base64.urlsafe_encode64("<<???>>", padding: false)  # => "PDw_Pz8-Pg"
Base64.urlsafe_decode64("PDw_Pz8-Pg")               # => "<<???>>"

urlsafe_decode64 akzeptiert Eingaben mit und ohne Padding; es ergänzt fehlende = und dekodiert dann strikt. Das Schlüsselwort padding: gibt es seit Ruby 2.3. Zu JWTs und anderen Tokens siehe Base64 vs. Base64URL, oder probieren Sie den Base64URL-Decoder.

So lesen Sie die Claims eines JWT, ohne es zu verifizieren:

require "json"

jwt = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0In0.sig"
header, payload, _sig = jwt.split(".")
JSON.parse(Base64.urlsafe_decode64(header))    # => {"alg" => "HS256"}
JSON.parse(Base64.urlsafe_decode64(payload))   # => {"sub" => "1234"}

Das Ausgabeformat {"alg" => "HS256"} ist das neue Hash#inspect von Ruby 3.4; ältere Versionen geben {"alg"=>"HS256"} aus.

Eine Datei kodieren und dekodieren

Lesen und schreiben Sie im Binärmodus, sonst können die Zeilenende-Umwandlung unter Windows und Kodierungskonvertierungen die Bytes verändern:

puts Base64.strict_encode64(File.binread("hello.txt"))   # aGVsbG8K
File.binwrite("copy.txt", Base64.strict_decode64("aGVsbG8K"))

Große Dateien in Blöcken

Das Modul Base64 hat keine Streaming-API, aber pack("m0") auf Blöcken funktioniert, solange jeder Block ein Vielfaches von 3 Bytes ist:

File.open("big.bin", "rb") do |src|
  File.open("big.b64", "wb") do |dst|
    while (chunk = src.read(3 * 1024 * 64))   # 192 KiB, divisible by 3
      dst.write([chunk].pack("m0"))
    end
  end
end

Bei einer 300-KB-Testdatei war das Ergebnis identisch mit strict_encode64(File.binread(...)). Bei jeder anderen Blockgröße erscheint =-Padding mitten in der Ausgabe.

pack und unpack1 direkt

Das Modul ist nur eine Komfortschicht. Diese Aufrufe sind gleichwertig und brauchen kein require:

["hi"].pack("m0")    # => "aGk="    (same as strict_encode64)
["hi"].pack("m")     # => "aGk=\n"  (same as encode64)
"aGk=".unpack1("m0") # => "hi"      (same as strict_decode64)

Validierung und Fehler

strict_decode64 wirft einen ArgumentError mit der Meldung invalid base64 für alles, was kein kanonisches Base64 mit Padding ist. Wir haben sechs Eingaben probiert, und alle lösten ihn aus:

EingabeProblem
PDw_Pz8-Pg==URL-sichere Zeichen
aGkfehlendes Padding
aGk=!!Datenmüll nach dem Padding
aunmögliche Länge
aGl=Padding-Bits ungleich null
"aGVs\nbG8K"Zeilenumbruch
def base64?(s)
  Base64.strict_decode64(s)
  true
rescue ArgumentError
  false
end

Das nicht strikte decode64 wirft nie einen Fehler. Es überspringt alles, was es nicht versteht, und liefert Datenmüll statt eines Fehlers:

Base64.decode64("aGk=!!")        # => "hi"
Base64.decode64("PDw_Pz8-Pg==")  # => "<<\x0F\xCF\xC3\xE0"   (should be "<<???>>")

Verwenden Sie decode64 nur für MIME-Inhalte mit Zeilenumbrüchen und nur, wenn Sie der Quelle vertrauen. Weigert sich ein String, sich dekodieren zu lassen, zeigt der Base64-Validator auf das Problem, und Häufige Base64-Fehler erklärt die üblichen Ursachen.

Ruby 3.4 und Bundler

Ruby 3.4 hat base64 von den Default Gems zu den Bundled Gems verschoben. Außerhalb von Bundler ändert sich nichts. Unter bundle exec scheitert ein Gemfile ohne das Gem:

warning: base64 was loaded from the standard library, but is not part of the
default gems starting from Ruby 3.4.0.
...
cannot load such file -- base64 (LoadError)

Nehmen Sie gem "base64" in das Gemfile oder die Gemspec auf, oder ersetzen Sie die Aufrufe durch pack("m0") / unpack1("m0"), die zum Kern von Ruby gehören.

Häufige Fragen

Warum fügt Base64.encode64 ein \n an meinen String an?

encode64 erzeugt Ausgabe nach RFC 2045 mit einem Zeilenumbruch alle 60 Zeichen und am Ende. Verwenden Sie strict_encode64 für Header, JSON und URLs; es fügt nie Zeilenumbrüche ein.

Warum erhalte ich unter Ruby 3.4 „cannot load such file -- base64“?

Seit Ruby 3.4 ist base64 ein Bundled Gem statt eines Default Gems. Ein einfaches ruby lädt es weiterhin, unter Bundler müssen Sie aber gem "base64" in das Gemfile aufnehmen. Oder Sie verzichten auf das require und verwenden [data].pack("m0").

Wie dekodiere ich in Ruby Base64 ohne Padding?

Base64.urlsafe_decode64 ergänzt fehlendes Padding selbst. Beim Standardalphabet hängen Sie vor dem Aufruf von strict_decode64 den Ausdruck "=" * (-s.length % 4) an.

Weiterlesen