Sichere Dateiverifikation mit Ruby und SHA384
Um einen Download in Ruby zu verifizieren, berechnen Sie mit dem SHA384-Digest von OpenSSL einen Hash seiner Bytes und vergleichen Sie das Ergebnis mit einer vertrauenswürdigen Prüfsumme. Das folgende Skript akzeptiert ein oder mehrere Datei-Prüfsummen-Paare und liefert einen Exit-Status ungleich null, wenn eine Prüfung fehlschlägt. So können Sie es in einem automatisierten Job einsetzen.
Mit einer vertrauenswürdigen Prüfsumme beginnen
SHA-384 gehört zur SHA-2-Familie und erzeugt einen 384-Bit-Digest: 48 Bytes oder 96 hexadezimale Zeichen. Verwenden Sie es, wenn Ihr Herausgeber oder bestehender Workflow SHA384-Prüfsummen bereitstellt. Eine mit SHA256 erzeugte Referenz lässt sich nicht mit SHA384 prüfen.
Beziehen Sie die erwartete Prüfsumme aus einer vertrauenswürdigen Quelle, etwa von der authentifizierten HTTPS-Releaseseite des Herausgebers oder aus einem signierten Prüfsummenmanifest, dessen Signatur Sie verifiziert haben. Wer sowohl die Datei als auch ihre Referenzprüfsumme ersetzen kann, kann Ihre Prüfung erfolgreich bestehen lassen. Hashing erkennt Änderungen gegenüber dieser Referenz. Es macht eine Datei weder manipulationssicher noch belegt es, dass sie sicher ausgeführt werden kann. Die OpenSSL-Dokumentation von Ruby erläutert, wie Digests auch als Bestandteil digitaler Signaturen dienen.
Ruby und OpenSSL prüfen
Sie benötigen Ruby mit seiner OpenSSL-Erweiterung. Die hier gezeigten Befehle wurden unter Linux mit
Ruby 3.4.10, Ruby/OpenSSL 3.3.3 und OpenSSL 3.6.4 getestet. Die Shell-Beispiele verwenden Bash;
sha384sum aus GNU Coreutils ist für den unabhängigen Vergleich weiter unten
optional. Falls Ruby fehlt, folgen Sie der offiziellen
Ruby-Installationsanleitung für Ihre Plattform.
Prüfen Sie den Interpreter, die eingebundene Bibliothek und die Verfügbarkeit von SHA384, bevor Sie fortfahren:
ruby -ropenssl -e 'puts RUBY_DESCRIPTION; puts "Ruby/OpenSSL #{OpenSSL::VERSION}"; puts OpenSSL::OPENSSL_LIBRARY_VERSION; puts "SHA384: #{OpenSSL::Digest.new("SHA384").digest_length} bytes"'
Die letzte Zeile sollte SHA384: 48 bytes lauten. Falls das Laden von OpenSSL oder das
Erstellen des Digests fehlschlägt, korrigieren Sie Ihre Ruby-Installation, bevor Sie das Prüfskript
ausführen. Der Befehl openssl in Ihrem PATH kann eine andere Bibliothek als Ruby
verwenden. Seine Version allein belegt daher nicht, dass diese Voraussetzung erfüllt ist.
Prüfskript speichern
Speichern Sie dieses vollständige Skript als verify_sha384.rb. Es liest jede Datei in
Blöcken von 8 KiB und verwendet den Puffer wieder, lädt also nicht die gesamte Datei in den
Arbeitsspeicher. Die Ruby-Methode
IO#read gibt am Dateiende
nil zurück, wenn eine positive Länge angegeben wird. Das gilt auch für eine
leere Datei. Jeder Block wird an
OpenSSL::Digest#update übergeben.
require 'openssl'
def sha384_hash(path)
digest = OpenSSL::Digest.new('SHA384')
File.open(path, 'rb') do |file|
buffer = ''.b
digest.update(buffer) while file.read(8192, buffer)
end
digest.hexdigest
end
if ARGV.empty? || ARGV.length.odd?
warn 'Usage: ruby verify_sha384.rb FILE SHA384 [FILE SHA384 ...]'
exit 2
end
failed = false
ARGV.each_slice(2) do |path, expected|
unless /\A[0-9a-fA-F]{96}\z/.match?(expected)
warn "#{path}: ERROR (expected 96 hexadecimal characters)"
failed = true
next
end
begin
actual = sha384_hash(path)
if actual == expected.downcase
puts "#{path}: OK"
else
warn "#{path}: FAILED (SHA384 mismatch)"
failed = true
end
rescue SystemCallError, IOError => error
warn "#{path}: ERROR (#{error.message})"
failed = true
end
end
exit(failed ? 1 : 0)
Übergeben Sie nur den Digest, ohne Dateinamen, Präfix sha384- oder umgebende
Leerraumzeichen. Hexadezimale Großbuchstaben werden akzeptiert. Das Skript vergleicht öffentliche
Prüfsummen mit ==. In diesem Workflow gibt es keine geheime Prüfsumme,
die vor einer Offenlegung durch Laufzeitunterschiede geschützt werden muss. Für geheime
Authentifikatoren wie HMACs bietet Ruby
Vergleichsmethoden mit konstanter Laufzeit,
statt eine selbst geschriebene Vergleichsschleife zu erfordern.
Eine Datei mit bekanntem Ergebnis verifizieren
Führen Sie die folgenden Befehle in Bash in einem temporären Arbeitsverzeichnis aus, das
verify_sha384.rb enthält. Sie erzeugen example.txt mit genau den drei
Bytes abc, ohne abschließenden Zeilenumbruch. Beim Erstellen wird eine
vorhandene Datei nicht überschrieben. Schlägt das Erstellen fehl, stoppt die Befehlskette mit
&& vor der Verifikation. Verwenden Sie ein anderes temporäres
Arbeitsverzeichnis, wenn Sie die Einrichtung wiederholen möchten.
ruby -e 'File.open("example.txt", "wbx") { |file| file.write("abc") }' &&
EXPECTED_SHA384='cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7' &&
ruby verify_sha384.rb example.txt "$EXPECTED_SHA384"
Das Ergebnis lautet:
example.txt: OK
Der Befehl endet mit dem Status 0. Für Ihren eigenen Download setzen Sie
dessen Pfad und die vertrauenswürdige SHA384-Prüfsumme für genau dieses Release ein. Setzen Sie Pfade
mit Leerzeichen in Anführungszeichen. Das Prüfskript öffnet Dateien nur zum Lesen und erstellt kein
Verifikationsartefakt.
Wenn Sie GNU Coreutils haben, berechnen Sie die Prüfsumme der Beispieldatei unabhängig mit
sha384sum:
sha384sum --binary -- example.txt
Das erste Ausgabefeld sollte EXPECTED_SHA384 entsprechen. Einen Hash aus einer gerade
heruntergeladenen Datei zu berechnen, ist für einen Vergleich nützlich, liefert für sich genommen
aber keine vertrauenswürdige Referenz für diesen Download.
Den Job bei einem Batch-Fehler fehlschlagen lassen
Verwenden Sie weiterhin dieselbe Bash-Sitzung, damit EXPECTED_SHA384 gesetzt bleibt.
Erstellen Sie eine leere Datei und prüfen Sie beide Beispieldateien, indem Sie ein weiteres
Datei-Prüfsummen-Paar übergeben:
ruby -e 'File.open("empty.bin", "wbx") {}' &&
EMPTY_SHA384='38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b' &&
ruby verify_sha384.rb example.txt "$EXPECTED_SHA384" empty.bin "$EMPTY_SHA384"
Beide Dateien sollten OK melden, und der Befehl endet mit
0. Auch beim Erstellen von empty.bin wird ein bereits
vorhandenes Ziel abgewiesen. Hängen Sie nun einen Zeilenumbruch an die erste, nur zu Testzwecken
erstellte Beispieldatei an und wiederholen Sie den Batch:
ruby -e 'File.open("example.txt", "ab") { |file| file.write("\n") }' &&
ruby verify_sha384.rb example.txt "$EXPECTED_SHA384" empty.bin "$EMPTY_SHA384"
example.txt meldet FAILED (SHA384 mismatch) auf der Standardfehlerausgabe,
während empty.bin weiterhin OK auf der Standardausgabe
meldet. Der Batch endet mit 1: Ein späterer Erfolg kann einen früheren
Fehler nicht aufheben. Das Skript prüft jedes übergebene Paar und überschreibt keine der beiden
Referenzprüfsummen.
Die Exit-Codes sind 0, wenn alle Dateien übereinstimmen,
1, wenn eine Datei nicht übereinstimmt, ihre erwartete Prüfsumme ein
ungültiges Format hat oder sie nicht gelesen werden kann, und 2, wenn
die Liste der Paare fehlt oder unvollständig ist. Setzen Sie das Prüfskript in einem Job als letzten
Befehl seines Schritts ein oder geben Sie seinen Status ungleich null explizit weiter, bevor Sie
nachfolgende Befehle ausführen. Eine Fehlermeldung auszugeben allein lässt einen Shell-Job nicht
fehlschlagen.
Eine fehlgeschlagene Prüfung diagnostizieren
- 96 hexadezimale Zeichen erwartet: Kopieren Sie nur den SHA384-Digest. Eine SHA256-Prüfsumme,
ein Base64-Wert oder eine vollständige Ausgabezeile von
sha384sumist kein gültiges Argument. - SHA384 stimmt nicht überein: Prüfen Sie das Release, den Dateinamen, die Vollständigkeit des Downloads und den erwarteten Algorithmus. Ersetzen Sie die Referenz nicht durch den neuen Hash, nur damit die Verifikation erfolgreich ist.
- Datei- oder Berechtigungsfehler: Prüfen Sie Arbeitsverzeichnis, Pfad und Leseberechtigungen
des Kontos, unter dem der Job läuft. Ein fehlgeschlagener Lesevorgang liefert
1, selbst wenn andere Dateien die Prüfung bestehen.
Die zu verifizierenden Bytes bewahren
Der Binärmodus bewahrt die Bytes, die dem Digest übergeben werden. Die
Dokumentation der Dateimodi in Ruby
beschreibt, wie 'b' die Umwandlung von Zeilenumbrüchen unter Windows
deaktiviert. Dadurch werden eine LF-Datei und eine CRLF-Datei nicht identisch: Sie enthalten
unterschiedliche Bytes und sollten unterschiedliche Prüfsummen haben. Vermeiden Sie es, vor dieser
Prüfung Zeilenenden zu normalisieren, Leerraumzeichen zu entfernen oder die Zeichencodierung zu
ändern. Solche Schritte würden transformierte Inhalte statt der heruntergeladenen Datei
verifizieren.
Verwenden Sie vollständig geschriebene reguläre Dateien, die während der Verifikation und der anschließenden Nutzung unverändert bleiben. Dieses kleine CLI sperrt keine Dateien und verhindert nicht, dass ein anderer Prozess sie nach einer erfolgreichen Prüfung ersetzt. Schützen Sie sowohl die vertrauenswürdige Referenz als auch die verifizierte Datei.
Zum Hashing innerhalb eines Transloadit-Workflows lesen Sie die Dokumentation zu /file/hash. Die Anforderung einer vertrauenswürdigen Referenz gilt auch, wenn eine Prüfsumme während der Dateiverarbeitung erzeugt wird.
