Czy istnieje prosty sposób generowania (i sprawdzania) sum kontrolnych MD5 listy plików w Pythonie? (Mam mały program, nad którym pracuję i chciałbym potwierdzić sumy kontrolne plików).
Trzymanie go w Pythonie ułatwia zarządzanie zgodnością między platformami.
Alexander,
Jeśli potrzebujesz rozwiązania z „paskiem postępu * lub podobnym (dla bardzo dużych plików), rozważ to rozwiązanie: stackoverflow.com/questions/1131220/…
Laurent LAPORTE
1
@kennytm Podany przez ciebie link mówi to w drugim akapicie: „Podstawowy algorytm MD5 nie jest już uważany za bezpieczny” podczas opisu md5sum. Właśnie dlatego, moim zdaniem, programiści dbający o bezpieczeństwo nie powinni go używać.
Debug255,
1
@ Debug255 Dobry i ważny punkt. md5sumNależy unikać zarówno techniki opisanej w tym pytaniu SO - lepiej, jeśli to możliwe, użyć SHA-2 lub SHA-3: en.wikipedia.org/wiki/Secure_Hash_Algorytm
Pamiętaj, że czasami nie będziesz w stanie zmieścić całego pliku w pamięci. W takim przypadku musisz sekwencyjnie czytać fragmenty 4096 bajtów i podawać je do md5metody:
Uwaga:hash_md5.hexdigest() zwróci reprezentację ciągu szesnastkowego dla skrótu, jeśli potrzebujesz tylko spakowanych bajtów return hash_md5.digest(), więc nie musisz konwertować z powrotem.
[(fname, hashlib.md5(file_as_bytes(open(fname,'rb'))).digest())for fname in fnamelst]
Przypomnijmy jednak, że MD5 jest znany jako uszkodzony i nie powinien być wykorzystywany do żadnych celów, ponieważ analiza podatności może być naprawdę trudna, a analiza ewentualnego przyszłego wykorzystania kodu w celu zapewnienia bezpieczeństwa jest niemożliwa. IMHO powinien być całkowicie usunięty z biblioteki, aby każdy, kto go używa, był zmuszony do aktualizacji. Oto, co powinieneś zrobić zamiast tego:
[(fname, hashlib.sha256(file_as_bytes(open(fname,'rb'))).digest())for fname in fnamelst]
Jeśli chcesz tylko 128 bitów skrótu, możesz to zrobić .digest()[:16].
To da ci listę krotek, każda krotka zawiera nazwę swojego pliku i jego skrót.
Ponownie mocno kwestionuję twoje użycie MD5. Powinieneś przynajmniej używać SHA1, i biorąc pod uwagę ostatnie błędy odkryte w SHA1 , prawdopodobnie nawet nie to. Niektórzy uważają, że dopóki nie używasz MD5 do celów „kryptograficznych”, nic ci nie jest. Ale rzeczy mają zwykle szerszy zakres, niż początkowo się spodziewasz, a Twoja przypadkowa analiza podatności może okazać się całkowicie wadliwa. Najlepiej jest po prostu przyzwyczaić się do używania właściwego algorytmu z bramki. Wystarczy wpisać inną wiązkę liter. To nie jest takie trudne.
Oto sposób, który jest bardziej złożony, ale wydajny pod względem pamięci :
Używam tylko MD5, aby potwierdzić, że plik nie jest uszkodzony. Nie martwię się tak bardzo, że zostanie zepsuta.
Alexander,
87
@TheLifelessOne: I pomimo @Omnifarious przerażających ostrzeżeń, jest to całkowicie dobre wykorzystanie MD5.
Prezydent James K. Polk,
22
@GregS, @TheLifelessOne - Tak, i następną rzeczą, o której wiesz, że ktoś znajdzie sposób na wykorzystanie tego faktu w Twojej aplikacji, aby spowodować, że plik zostanie zaakceptowany jako nieuszkodzony, jeśli nie jest to plik, którego się w ogóle spodziewasz. Nie, stoję przy moich przerażających ostrzeżeniach. Myślę, że MD5 powinien zostać usunięty lub zawierać ostrzeżenia o wycofaniu.
Wszechobecny
10
Prawdopodobnie użyłbym .hexdigest () zamiast .digest () - ludzie łatwiej czytają - co jest celem OP.
zbstof
21
Użyłem tego rozwiązania, ale niepoprawnie dał ten sam skrót dla dwóch różnych plików pdf. Rozwiązaniem było otwarcie plików przez określenie trybu binarnego, tj .: [(fname, hashlib.md5 (open (fname, 'rb' ) .read ()). Hexdigest ()) dla fname w fnamelst] Jest to bardziej powiązane do funkcji otwartej niż md5, ale pomyślałem, że przydałoby się to zgłosić, biorąc pod uwagę powyższy wymóg zgodności między platformami (patrz także: docs.python.org/2/tutorial/... ).
BlueCoder
34
Najwyraźniej nie dodam niczego zasadniczo nowego, ale dodałem tę odpowiedź, zanim osiągnąłem status komentowania, a regiony kodu wyjaśniają wszystko - w każdym razie, w szczególności, aby odpowiedzieć na pytanie @ Nemo z odpowiedzi Omnifariousa:
Zdarzyło mi się trochę zastanowić nad sumami kontrolnymi (konkretnie przyjechałem tutaj, szukając sugestii dotyczących rozmiarów bloków) i odkryłem, że ta metoda może być szybsza, niż można się było spodziewać. Biorąc najszybszy (ale dość typowy) timeit.timeitlub /usr/bin/timewynik z każdej z kilku metod sumowania pliku o wielkości ok. 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
Wygląda więc na to, że zarówno plik Python, jak i / usr / bin / md5sum zajmuje około 30 ms dla pliku 11 MB. Odpowiednia md5sumfunkcja ( md5sum_readw powyższej liście) jest bardzo podobna do funkcji Omnifarious:
To prawda, że pochodzą z pojedynczych przebiegów ( mmap te są zawsze o kilka razy szybsze, gdy wykonuje się co najmniej kilkadziesiąt przebiegów), a mój zwykle dostaje dodatkowy f.read(blocksize)po wyczerpaniu bufora, ale jest dość powtarzalny i pokazuje, że md5sumw wierszu poleceń jest niekoniecznie szybsze niż implementacja Pythona ...
EDYCJA: Przepraszam za duże opóźnienie, nie patrzyłem na to od jakiegoś czasu, ale aby odpowiedzieć na pytanie @ EdRandall, napiszę implementację Adler32. Nie uruchomiłem jednak testów porównawczych. Jest to w zasadzie to samo, co byłby CRC32: zamiast wywołań init, update i skrót wszystko jest zlib.adler32()połączeniem:
Zauważ, że musi to zaczynać się od pustego łańcucha, ponieważ sumy Adlera rzeczywiście różnią się, gdy zaczynają się od zera w porównaniu do ich sumy "", czyli 1- CRC może 0zamiast tego zacząć . ANDJest potrzebne -ing, aby to 32-bitowa liczba całkowita bez znaku, który zapewnia, że zwróci tę samą wartość całej wersjach Pythona.
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
Rozważyć użycie hashlib.blake2bzamiast md5(wystarczy wymienić md5ze blake2bw powyższym fragmencie). Jest kryptograficznie bezpieczny i szybszy niż MD5.
Cześć! Dodaj wyjaśnienie do swojego kodu, dlaczego jest to rozwiązanie problemu. Co więcej, ten post jest dość stary, więc powinieneś również dodać trochę informacji, dlaczego twoje rozwiązanie dodaje coś, czego inni jeszcze nie zajęli.
d_kennetz
1
To kolejny nieefektywny sposób na pamięć
Erik Aronesty
-2
Myślę, że poleganie na pakiecie invoke i pliku binarnym md5sum jest nieco wygodniejsze niż podproces lub pakiet md5
md5sum
?md5sum
. Właśnie dlatego, moim zdaniem, programiści dbający o bezpieczeństwo nie powinni go używać.md5sum
Należy unikać zarówno techniki opisanej w tym pytaniu SO - lepiej, jeśli to możliwe, użyć SHA-2 lub SHA-3: en.wikipedia.org/wiki/Secure_Hash_AlgorytmOdpowiedzi:
Możesz użyć hashlib.md5 ()
Pamiętaj, że czasami nie będziesz w stanie zmieścić całego pliku w pamięci. W takim przypadku musisz sekwencyjnie czytać fragmenty 4096 bajtów i podawać je do
md5
metody:Uwaga:
hash_md5.hexdigest()
zwróci reprezentację ciągu szesnastkowego dla skrótu, jeśli potrzebujesz tylko spakowanych bajtówreturn hash_md5.digest()
, więc nie musisz konwertować z powrotem.źródło
Jest sposób, który jest dość nieefektywny pamięci .
pojedynczy plik:
lista plików:
Przypomnijmy jednak, że MD5 jest znany jako uszkodzony i nie powinien być wykorzystywany do żadnych celów, ponieważ analiza podatności może być naprawdę trudna, a analiza ewentualnego przyszłego wykorzystania kodu w celu zapewnienia bezpieczeństwa jest niemożliwa. IMHO powinien być całkowicie usunięty z biblioteki, aby każdy, kto go używa, był zmuszony do aktualizacji. Oto, co powinieneś zrobić zamiast tego:
Jeśli chcesz tylko 128 bitów skrótu, możesz to zrobić
.digest()[:16]
.To da ci listę krotek, każda krotka zawiera nazwę swojego pliku i jego skrót.
Ponownie mocno kwestionuję twoje użycie MD5. Powinieneś przynajmniej używać SHA1, i biorąc pod uwagę ostatnie błędy odkryte w SHA1 , prawdopodobnie nawet nie to. Niektórzy uważają, że dopóki nie używasz MD5 do celów „kryptograficznych”, nic ci nie jest. Ale rzeczy mają zwykle szerszy zakres, niż początkowo się spodziewasz, a Twoja przypadkowa analiza podatności może okazać się całkowicie wadliwa. Najlepiej jest po prostu przyzwyczaić się do używania właściwego algorytmu z bramki. Wystarczy wpisać inną wiązkę liter. To nie jest takie trudne.
Oto sposób, który jest bardziej złożony, ale wydajny pod względem pamięci :
I znowu, ponieważ MD5 jest zepsuty i tak naprawdę nie powinien być nigdy używany:
Ponownie możesz
[:16]
po zakończeniu rozmowy ustawić,hash_bytestr_iter(...)
jeśli chcesz tylko 128 bitów wartości.źródło
Najwyraźniej nie dodam niczego zasadniczo nowego, ale dodałem tę odpowiedź, zanim osiągnąłem status komentowania, a regiony kodu wyjaśniają wszystko - w każdym razie, w szczególności, aby odpowiedzieć na pytanie @ Nemo z odpowiedzi Omnifariousa:
Zdarzyło mi się trochę zastanowić nad sumami kontrolnymi (konkretnie przyjechałem tutaj, szukając sugestii dotyczących rozmiarów bloków) i odkryłem, że ta metoda może być szybsza, niż można się było spodziewać. Biorąc najszybszy (ale dość typowy)
timeit.timeit
lub/usr/bin/time
wynik z każdej z kilku metod sumowania pliku o wielkości ok. 11 MB:Wygląda więc na to, że zarówno plik Python, jak i / usr / bin / md5sum zajmuje około 30 ms dla pliku 11 MB. Odpowiednia
md5sum
funkcja (md5sum_read
w powyższej liście) jest bardzo podobna do funkcji Omnifarious:To prawda, że pochodzą z pojedynczych przebiegów (
mmap
te są zawsze o kilka razy szybsze, gdy wykonuje się co najmniej kilkadziesiąt przebiegów), a mój zwykle dostaje dodatkowyf.read(blocksize)
po wyczerpaniu bufora, ale jest dość powtarzalny i pokazuje, żemd5sum
w wierszu poleceń jest niekoniecznie szybsze niż implementacja Pythona ...EDYCJA: Przepraszam za duże opóźnienie, nie patrzyłem na to od jakiegoś czasu, ale aby odpowiedzieć na pytanie @ EdRandall, napiszę implementację Adler32. Nie uruchomiłem jednak testów porównawczych. Jest to w zasadzie to samo, co byłby CRC32: zamiast wywołań init, update i skrót wszystko jest
zlib.adler32()
połączeniem:Zauważ, że musi to zaczynać się od pustego łańcucha, ponieważ sumy Adlera rzeczywiście różnią się, gdy zaczynają się od zera w porównaniu do ich sumy
""
, czyli1
- CRC może0
zamiast tego zacząć .AND
Jest potrzebne -ing, aby to 32-bitowa liczba całkowita bez znaku, który zapewnia, że zwróci tę samą wartość całej wersjach Pythona.źródło
W Python 3.8+ możesz to zrobić
Rozważyć użycie
hashlib.blake2b
zamiastmd5
(wystarczy wymienićmd5
zeblake2b
w powyższym fragmencie). Jest kryptograficznie bezpieczny i szybszy niż MD5.źródło
:=
Operator jest ona "przydział" (New Python 3.8+); pozwala przypisać wartości do większego wyrażenia; więcej informacji tutaj: docs.python.org/3/whatsnew/3.8.html#assignment-expressionsźródło
Myślę, że poleganie na pakiecie invoke i pliku binarnym md5sum jest nieco wygodniejsze niż podproces lub pakiet md5
To oczywiście zakłada, że masz invoke i zainstalowany md5sum.
źródło
path
jest to ścieżka podana przez użytkownika, pozwoli to każdemu użytkownikowi wykonać dowolne polecenia bash w twoim systemie.