Gibt es eine einfache Möglichkeit, MD5-Prüfsummen einer Dateiliste in Python zu generieren (und zu überprüfen)? (Ich habe ein kleines Programm, an dem ich arbeite, und ich möchte die Prüfsummen der Dateien bestätigen.)
Wenn Sie es in Python behalten, können Sie die plattformübergreifende Kompatibilität einfacher verwalten.
Alexander
Wenn Sie eine Lösung mit "Fortschrittsbalken * oder ähnlichem (für sehr große Dateien) wünschen
Laurent LAPORTE
1
@kennytm Der von Ihnen angegebene Link besagt dies im zweiten Absatz: "Der zugrunde liegende MD5-Algorithmus wird bei der Beschreibung nicht mehr als sicher angesehen" md5sum. Deshalb sollten sicherheitsbewusste Programmierer es meiner Meinung nach nicht verwenden.
Debug255
1
@ Debug255 Guter und gültiger Punkt. Beides md5sumund die in dieser SO-Frage beschriebene Technik sollten vermieden werden - es ist besser, wenn möglich SHA-2 oder SHA-3 zu verwenden: en.wikipedia.org/wiki/Secure_Hash_Algorithms
Beachten Sie, dass Sie manchmal nicht in der Lage sind, die gesamte Datei in den Speicher zu passen. In diesem Fall müssen Sie Blöcke von 4096 Bytes nacheinander lesen und der md5Methode zuführen :
Hinweis:hash_md5.hexdigest() Gibt die Hex-Zeichenfolgendarstellung für den Digest zurück, wenn Sie nur die gepackten Bytes verwenden müssen return hash_md5.digest(), damit Sie nicht zurück konvertieren müssen.
[(fname, hashlib.md5(file_as_bytes(open(fname,'rb'))).digest())for fname in fnamelst]
Denken Sie jedoch daran, dass MD5 als fehlerhaft bekannt ist und für keinen Zweck verwendet werden sollte, da die Schwachstellenanalyse sehr schwierig sein kann und eine Analyse der möglichen zukünftigen Verwendung Ihres Codes für Sicherheitsprobleme unmöglich ist. IMHO sollte es direkt aus der Bibliothek entfernt werden, damit jeder, der es verwendet, gezwungen ist, es zu aktualisieren. Folgendes sollten Sie stattdessen tun:
[(fname, hashlib.sha256(file_as_bytes(open(fname,'rb'))).digest())for fname in fnamelst]
Wenn Sie nur Digest im Wert von 128 Bit möchten, können Sie dies tun .digest()[:16].
Dadurch erhalten Sie eine Liste von Tupeln, wobei jedes Tupel den Namen seiner Datei und seinen Hash enthält.
Auch hier stelle ich Ihre Verwendung von MD5 in Frage. Sie sollten mindestens SHA1 verwenden und angesichts der kürzlich in SHA1 entdeckten Fehler wahrscheinlich nicht einmal das. Einige Leute denken, dass es Ihnen gut geht, solange Sie MD5 nicht für "kryptografische" Zwecke verwenden. Aber Dinge haben die Tendenz, einen größeren Umfang zu haben, als Sie ursprünglich erwartet hatten, und Ihre gelegentliche Schwachstellenanalyse kann sich als völlig fehlerhaft erweisen. Es ist am besten, sich einfach daran zu gewöhnen, den richtigen Algorithmus von Anfang an zu verwenden. Es ist nur das Tippen eines anderen Buchstabenbündels. Es ist nicht so schwer.
Hier ist ein Weg, der komplexer, aber speichereffizienter ist :
Ich verwende nur MD5, um zu bestätigen, dass die Datei nicht beschädigt ist. Ich bin nicht so besorgt darüber, dass es kaputt geht.
Alexander
87
@TheLifelessOne: Und trotz @Omnifarious beängstigender Warnungen ist das eine gute Verwendung von MD5.
Präsident James K. Polk
22
@GregS, @TheLifelessOne - Ja, und als nächstes wissen Sie, dass jemand einen Weg findet, diese Tatsache über Ihre Anwendung zu nutzen, um zu bewirken, dass eine Datei als unbeschädigt akzeptiert wird, wenn es sich überhaupt nicht um die erwartete Datei handelt. Nein, ich stehe zu meinen gruseligen Warnungen. Ich denke, MD5 sollte entfernt werden oder mit Verfallswarnungen kommen.
Omnifarious
10
Ich würde wahrscheinlich .hexdigest () anstelle von .digest () verwenden - es ist für Menschen einfacher zu lesen - was der Zweck von OP ist.
Zbstof
21
Ich habe diese Lösung verwendet, aber sie hat nicht korrekt den gleichen Hash für zwei verschiedene PDF-Dateien angegeben. Die Lösung bestand darin, die Dateien durch Angabe des Binärmodus zu öffnen, dh: [(fname, hashlib.md5 (open (fname, 'rb' ) .read ()). Hexdigest ()) für fname in fnamelst] Dies ist verwandter für die offene Funktion als md5, aber ich dachte, es könnte nützlich sein, sie zu melden, da die oben angegebene plattformübergreifende Kompatibilität erforderlich ist (siehe auch: docs.python.org/2/tutorial/… ).
BlueCoder
34
Ich füge eindeutig nichts grundlegend Neues hinzu, aber ich habe diese Antwort hinzugefügt, bevor ich den Status eines Kommentars erreicht habe, und die Code-Regionen machen die Dinge klarer - jedenfalls speziell, um die Frage von @ Nemo aus der Antwort von Omnifarious zu beantworten:
Ich habe zufällig ein wenig über Prüfsummen nachgedacht (bin hierher gekommen, um nach Vorschlägen für Blockgrößen zu suchen) und habe festgestellt, dass diese Methode möglicherweise schneller ist als erwartet. Nehmen Sie die schnellste (aber ziemlich typische) timeit.timeitoder /usr/bin/timeErgebnis aus jeder von mehreren Methoden der Prüfsumme einer Datei von ca. 11 MB:
$ ./sum_methods.py
crc32_mmap(filename)0.0241742134094
crc32_read(filename)0.0219960212708
subprocess.check_output(['cksum', filename])0.0553209781647
md5sum_mmap(filename)0.0286180973053
md5sum_read(filename)0.0311000347137
subprocess.check_output(['md5sum', filename])0.0332629680634
$ time md5sum /tmp/test.data.300k
d3fe3d5d4c2460b5daacc30c6efbc77f /tmp/test.data.300k
real 0m0.043s
user 0m0.032s
sys 0m0.010s
$ stat -c '%s'/tmp/test.data.300k11890400
Es sieht also so aus, als würden sowohl Python als auch / usr / bin / md5sum etwa 30 ms für eine 11-MB-Datei benötigen. Die relevante md5sumFunktion ( md5sum_readin der obigen Auflistung) ist der von Omnifarious ziemlich ähnlich:
Zugegeben, diese stammen aus einzelnen Läufen (die mmapsind immer ein bisschen schneller, wenn mindestens ein paar Dutzend Läufe gemacht werden), und meine haben normalerweise ein Extra, f.read(blocksize)nachdem der Puffer erschöpft ist, aber es ist einigermaßen wiederholbar und zeigt, dass dies md5sumin der Befehlszeile der Fall ist nicht unbedingt schneller als eine Python-Implementierung ...
EDIT: Entschuldigung für die lange Verzögerung, ich habe mir das seit einiger Zeit nicht mehr angesehen, aber um die Frage von @ EdRandall zu beantworten, schreibe ich eine Adler32-Implementierung auf. Allerdings habe ich die Benchmarks dafür nicht ausgeführt. Es ist im Grunde das gleiche wie beim CRC32: Anstelle der Init-, Update- und Digest-Aufrufe ist alles ein zlib.adler32()Aufruf:
Beachten Sie, dass dies mit der leeren Zeichenfolge beginnen muss, da sich die Adler-Summen tatsächlich unterscheiden, wenn Sie bei Null beginnen, gegenüber ihrer Summe für "", 1dh - CRC kann 0stattdessen beginnen. Das AND-ing wird benötigt, um eine 32-Bit-Ganzzahl ohne Vorzeichen zu erstellen, wodurch sichergestellt wird, dass in allen Python-Versionen derselbe Wert zurückgegeben wird.
Könnten Sie möglicherweise ein paar Zeilen hinzufügen, die SHA1 und vielleicht auch zlib.adler32 vergleichen?
Ed Randall
1
Die obige Funktion md5sum () setzt voraus, dass Sie Schreibzugriff auf die Datei haben. Wenn Sie "r + b" im Aufruf von open () durch "rb" ersetzen, funktioniert dies einwandfrei.
import hashlib
with open("your_filename.txt","rb")as f:
file_hash = hashlib.md5()while chunk := f.read(8192):
file_hash.update(chunk)print(file_hash.digest())print(file_hash.hexdigest())# to get a printable str instead of bytes
Erwägen Sie hashlib.blake2bstatt md5(nur ersetzen md5mit blake2bin der obigen Snippet). Es ist kryptografisch sicher und schneller als MD5.
Hallo! Bitte fügen Sie Ihrem Code eine Erklärung hinzu, warum dies eine Lösung für das Problem ist. Darüber hinaus ist dieser Beitrag ziemlich alt, daher sollten Sie auch einige Informationen hinzufügen, warum Ihre Lösung etwas hinzufügt, das die anderen noch nicht angesprochen haben.
d_kennetz
1
Es ist ein weiterer ineffizienter Speicher
Erik Aronesty
-2
Ich denke, dass es etwas bequemer ist, sich auf invoke package und md5sum binary zu verlassen als auf subprocess oder md5 package
md5sum
?md5sum
. Deshalb sollten sicherheitsbewusste Programmierer es meiner Meinung nach nicht verwenden.md5sum
und die in dieser SO-Frage beschriebene Technik sollten vermieden werden - es ist besser, wenn möglich SHA-2 oder SHA-3 zu verwenden: en.wikipedia.org/wiki/Secure_Hash_AlgorithmsAntworten:
Sie können hashlib.md5 () verwenden
Beachten Sie, dass Sie manchmal nicht in der Lage sind, die gesamte Datei in den Speicher zu passen. In diesem Fall müssen Sie Blöcke von 4096 Bytes nacheinander lesen und der
md5
Methode zuführen :Hinweis:
hash_md5.hexdigest()
Gibt die Hex-Zeichenfolgendarstellung für den Digest zurück, wenn Sie nur die gepackten Bytes verwenden müssenreturn hash_md5.digest()
, damit Sie nicht zurück konvertieren müssen.quelle
Es gibt einen Weg, der ziemlich ineffizient ist .
einzelne Datei:
Liste der Dateien:
Denken Sie jedoch daran, dass MD5 als fehlerhaft bekannt ist und für keinen Zweck verwendet werden sollte, da die Schwachstellenanalyse sehr schwierig sein kann und eine Analyse der möglichen zukünftigen Verwendung Ihres Codes für Sicherheitsprobleme unmöglich ist. IMHO sollte es direkt aus der Bibliothek entfernt werden, damit jeder, der es verwendet, gezwungen ist, es zu aktualisieren. Folgendes sollten Sie stattdessen tun:
Wenn Sie nur Digest im Wert von 128 Bit möchten, können Sie dies tun
.digest()[:16]
.Dadurch erhalten Sie eine Liste von Tupeln, wobei jedes Tupel den Namen seiner Datei und seinen Hash enthält.
Auch hier stelle ich Ihre Verwendung von MD5 in Frage. Sie sollten mindestens SHA1 verwenden und angesichts der kürzlich in SHA1 entdeckten Fehler wahrscheinlich nicht einmal das. Einige Leute denken, dass es Ihnen gut geht, solange Sie MD5 nicht für "kryptografische" Zwecke verwenden. Aber Dinge haben die Tendenz, einen größeren Umfang zu haben, als Sie ursprünglich erwartet hatten, und Ihre gelegentliche Schwachstellenanalyse kann sich als völlig fehlerhaft erweisen. Es ist am besten, sich einfach daran zu gewöhnen, den richtigen Algorithmus von Anfang an zu verwenden. Es ist nur das Tippen eines anderen Buchstabenbündels. Es ist nicht so schwer.
Hier ist ein Weg, der komplexer, aber speichereffizienter ist :
Und wieder, da MD5 kaputt ist und eigentlich nie mehr verwendet werden sollte:
Auch hier können Sie
[:16]
nach dem Aufruf setzen,hash_bytestr_iter(...)
wenn Sie nur 128 Bit Digest wollen.quelle
Ich füge eindeutig nichts grundlegend Neues hinzu, aber ich habe diese Antwort hinzugefügt, bevor ich den Status eines Kommentars erreicht habe, und die Code-Regionen machen die Dinge klarer - jedenfalls speziell, um die Frage von @ Nemo aus der Antwort von Omnifarious zu beantworten:
Ich habe zufällig ein wenig über Prüfsummen nachgedacht (bin hierher gekommen, um nach Vorschlägen für Blockgrößen zu suchen) und habe festgestellt, dass diese Methode möglicherweise schneller ist als erwartet. Nehmen Sie die schnellste (aber ziemlich typische)
timeit.timeit
oder/usr/bin/time
Ergebnis aus jeder von mehreren Methoden der Prüfsumme einer Datei von ca. 11 MB:Es sieht also so aus, als würden sowohl Python als auch / usr / bin / md5sum etwa 30 ms für eine 11-MB-Datei benötigen. Die relevante
md5sum
Funktion (md5sum_read
in der obigen Auflistung) ist der von Omnifarious ziemlich ähnlich:Zugegeben, diese stammen aus einzelnen Läufen (die
mmap
sind immer ein bisschen schneller, wenn mindestens ein paar Dutzend Läufe gemacht werden), und meine haben normalerweise ein Extra,f.read(blocksize)
nachdem der Puffer erschöpft ist, aber es ist einigermaßen wiederholbar und zeigt, dass diesmd5sum
in der Befehlszeile der Fall ist nicht unbedingt schneller als eine Python-Implementierung ...EDIT: Entschuldigung für die lange Verzögerung, ich habe mir das seit einiger Zeit nicht mehr angesehen, aber um die Frage von @ EdRandall zu beantworten, schreibe ich eine Adler32-Implementierung auf. Allerdings habe ich die Benchmarks dafür nicht ausgeführt. Es ist im Grunde das gleiche wie beim CRC32: Anstelle der Init-, Update- und Digest-Aufrufe ist alles ein
zlib.adler32()
Aufruf:Beachten Sie, dass dies mit der leeren Zeichenfolge beginnen muss, da sich die Adler-Summen tatsächlich unterscheiden, wenn Sie bei Null beginnen, gegenüber ihrer Summe für
""
,1
dh - CRC kann0
stattdessen beginnen. DasAND
-ing wird benötigt, um eine 32-Bit-Ganzzahl ohne Vorzeichen zu erstellen, wodurch sichergestellt wird, dass in allen Python-Versionen derselbe Wert zurückgegeben wird.quelle
In Python 3.8+ können Sie dies tun
Erwägen Sie
hashlib.blake2b
stattmd5
(nur ersetzenmd5
mitblake2b
in der obigen Snippet). Es ist kryptografisch sicher und schneller als MD5.quelle
:=
Operator ist ein "Zuweisungsoperator" (neu in Python 3.8+). Sie können damit Werte innerhalb eines größeren Ausdrucks zuweisen. Weitere Informationen hier: docs.python.org/3/whatsnew/3.8.html#assignment-expressionsquelle
Ich denke, dass es etwas bequemer ist, sich auf invoke package und md5sum binary zu verlassen als auf subprocess oder md5 package
Dies setzt natürlich voraus, dass Sie invoke und md5sum installiert haben.
quelle
path
ein vom Benutzer angegebener Pfad vorhanden ist, kann jeder Benutzer beliebige Bash-Befehle auf Ihrem System ausführen.