Bir dosyanın MD5 sağlama toplamı oluşturma


348

Python'da bir dosya listesinin MD5 sağlama toplamlarını oluşturmanın (ve kontrol etmenin) basit bir yolu var mı? (Üzerinde çalıştığım küçük bir programım var ve dosyaların sağlama toplamlarını onaylamak istiyorum).


3
Neden sadece kullanmıyorsunuz md5sum?
kennytm

99
Python'da tutmak, platformlar arası uyumluluğu yönetmeyi kolaylaştırır.
Alexander

"İlerleme çubuğu * veya benzeri bir çözüm bulmak istiyorsanız (çok büyük dosyalar için), şu çözümü göz önünde bulundurun: stackoverflow.com/questions/1131220/…
Laurent LAPORTE

1
@kennytm Sağladığınız bağlantı ikinci paragrafta bunu söylüyor: "Altta yatan MD5 algoritması artık güvenli kabul edilmiyor" md5sum. Bu yüzden güvenlik bilincine sahip programcılar bence bunu kullanmamalıdır.
Debug255

1
@ Debug255 İyi ve geçerli bir nokta. Hem md5sumbu SO sorusunda açıklanan teknikten kaçınılmalıdır - mümkünse SHA-2 veya SHA-3 kullanmak daha iyidir: en.wikipedia.org/wiki/Secure_Hash_Algorithms
Per Lundberg

Yanıtlar:


464

Hashlib.md5 () kullanabilirsiniz

Bazen tüm dosyayı belleğe sığamayacağınızı unutmayın. Bu durumda, 4096 baytlık parçaları sırayla okumalı ve bunları md5yönteme beslemelisiniz :

import hashlib
def md5(fname):
    hash_md5 = hashlib.md5()
    with open(fname, "rb") as f:
        for chunk in iter(lambda: f.read(4096), b""):
            hash_md5.update(chunk)
    return hash_md5.hexdigest()

Not: paketlenmiş bayt kullanımına ihtiyacınız varsa, özet için onaltılık dize temsilini hash_md5.hexdigest()döndürür , böylece geri dönüştürmeniz gerekmez.return hash_md5.digest()


297

Oldukça bellek verimsiz bir yol var .

tek dosya:

import hashlib
def file_as_bytes(file):
    with file:
        return file.read()

print hashlib.md5(file_as_bytes(open(full_path, 'rb'))).hexdigest()

dosya listesi:

[(fname, hashlib.md5(file_as_bytes(open(fname, 'rb'))).digest()) for fname in fnamelst]

Bununla birlikte, MD5'in bozuk olduğu ve herhangi bir amaç için kullanılmaması gerektiğini unutmayın, çünkü güvenlik açığı analizi gerçekten zor olabilir ve güvenlik sorunları için kodunuzun gelecekte olabilecek olası kullanımlarını analiz etmek imkansızdır. IMHO, kütüphaneden kaldırılmalıdır, böylece onu kullanan herkes güncellemek zorunda kalır. İşte bunun yerine yapmanız gerekenler:

[(fname, hashlib.sha256(file_as_bytes(open(fname, 'rb'))).digest()) for fname in fnamelst]

Sadece 128 bit değerinde sindirim istiyorsanız yapabilirsiniz .digest()[:16].

Bu, her bir dosyanın kendi adını ve karmasını içeren bir grup listesi verecektir.

Yine MD5 kullanımınızı şiddetle sorgularım. En azından SHA1'i kullanmalısınız ve SHA1'de keşfedilen son kusurları vermelisiniz , muhtemelen bu bile değil. Bazı insanlar MD5'i 'kriptografik' amaçlar için kullanmadığınız sürece iyi olduğunuzu düşünüyorlar. Ancak, işlerin kapsamda başlangıçta beklediğinizden daha geniş olma eğilimi vardır ve geçici güvenlik açığı analiziniz tamamen kusurlu olabilir. Kapıdan doğru algoritmayı kullanma alışkanlığına sahip olmak en iyisidir. Sadece farklı bir grup harf yazmak yeterlidir. O kadar zor değil.

İşte daha karmaşık, ancak bellek verimli bir yol :

import hashlib

def hash_bytestr_iter(bytesiter, hasher, ashexstr=False):
    for block in bytesiter:
        hasher.update(block)
    return hasher.hexdigest() if ashexstr else hasher.digest()

def file_as_blockiter(afile, blocksize=65536):
    with afile:
        block = afile.read(blocksize)
        while len(block) > 0:
            yield block
            block = afile.read(blocksize)


[(fname, hash_bytestr_iter(file_as_blockiter(open(fname, 'rb')), hashlib.md5()))
    for fname in fnamelst]

Ve yine, MD5 bozulduğundan ve artık gerçekten kullanılmaması gerektiğinden:

[(fname, hash_bytestr_iter(file_as_blockiter(open(fname, 'rb')), hashlib.sha256()))
    for fname in fnamelst]

Yine, sadece 128 bitlik sindirim istiyorsanız [:16], çağrıdan sonra koyabilirsiniz hash_bytestr_iter(...).


66
Yalnızca dosyanın bozuk olmadığını onaylamak için MD5 kullanıyorum. Kırılmasından endişe etmiyorum.
Alexander

87
@TheLifelessOne: @Omnifarious korkunç uyarılara rağmen, bu MD5'in mükemmel bir şekilde kullanılmasıdır.
Başkan James K. Polk

22
@GregS, @TheLifelessOne - Evet ve bildiğiniz bir sonraki şey, uygulamanızla ilgili bu gerçeği, bir dosyanın beklediğiniz dosya olmadığında bozulmamış olarak kabul edilmesine neden olmak için bir yol bulur. Hayır, korkutucu uyarılarımın yanındayım. Bence MD5 kaldırılmalı veya kullanımdan kaldırma uyarıları ile gelmelidir.
şeye kadir

10
Muhtemelen .digest () yerine .hexdigest () kullanırdım - insanların okuması daha kolaydır - OP'nin amacı budur.
zbstof

21
Bu çözümü kullandım ama yanlış iki farklı pdf dosyası için aynı karma verdi. Çözüm dosyaları ikili mod belirterek açmaktı, yani: [(fname, hashlib.md5 (open (fname, 'rb' ) .read ()). Hexdigest ()) fnamelst'deki fname için] Bu daha ilgili md5'ten daha açık bir işleve rağmen, yukarıda belirtilen platformlar arası uyumluluk gereksinimi nedeniyle bunu bildirmenin yararlı olabileceğini düşündüm (ayrıca bkz: docs.python.org/2/tutorial/… ).
BlueCoder

34

Açıkça temelde yeni bir şey eklemiyorum, ancak durumu yorumlamadan önce bu cevabı ekledim, ayrıca kod bölgeleri her şeyi daha net hale getiriyor - her neyse, özellikle Omnifarious'ın cevabından @ Nemo'nun sorusuna cevap vermek için:

Checksum'ları biraz düşündüm (buraya özellikle blok boyutları hakkında öneriler aramaya geldim) ve bu yöntemin beklediğinizden daha hızlı olabileceğini buldum. En hızlı (ama oldukça tipik) timeit.timeitveya /usr/bin/timeyakl. 11MB:

$ ./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.300k
11890400

Yani, hem Python hem de / usr / bin / md5sum 11MB'lık bir dosya için yaklaşık 30ms sürer gibi görünüyor. İlgili md5sumişlev ( md5sum_readyukarıdaki listede) Omnifarious'ın işlevine oldukça benzer:

import hashlib
def md5sum(filename, blocksize=65536):
    hash = hashlib.md5()
    with open(filename, "rb") as f:
        for block in iter(lambda: f.read(blocksize), b""):
            hash.update(block)
    return hash.hexdigest()

Verilmiş, bunlar tekli çalışmalardan ( mmapen az birkaç düzine çalışma yapıldığında her zaman daha hızlıdır) ve f.read(blocksize)arabellek tükendikten sonra mayın genellikle fazladan var , ancak makul bir şekilde tekrarlanabilir ve md5sumkomut satırında olduğunu gösterir. Python uygulamasından daha hızlı olması gerekmez ...

DÜZENLEME: Uzun gecikme için özür dilerim, bir süredir buna bakmadım, ancak @ EdRandall'ın sorusunu yanıtlamak için bir Adler32 uygulaması yazacağım. Ancak, bunun için kriterleri çalıştırmadım. Temel olarak CRC32'ninkiyle aynı olurdu: çağrıları başlatmak, güncellemek ve özetlemek yerine her şey bir zlib.adler32()çağrıdır:

import zlib
def adler32sum(filename, blocksize=65536):
    checksum = zlib.adler32("")
    with open(filename, "rb") as f:
        for block in iter(lambda: f.read(blocksize), b""):
            checksum = zlib.adler32(block, checksum)
    return checksum & 0xffffffff

Adler toplamları, sıfırdan başlayarak toplamlarına göre gerçekten farklı olduğu için bunun boş dize ile başlaması gerektiğini unutmayın "", yani 1- CRC bununla başlayabilir 0. ANDİng buna Python sürümleri arasında aynı değeri döndürür sağlayan 32 bit işaretsiz tam sayı yapmak için gereklidir.


SHA1'i karşılaştıran birkaç satır ekleyebilir misiniz ve ayrıca zlib.adler32 olabilir mi?
Ed Randall

1
Yukarıdaki md5sum () işlevi, dosyaya yazma erişiminiz olduğunu varsayar. Open () çağrısında "r + b" yi "rb" ile değiştirirseniz iyi çalışır.
Kevin Lyda

1
@EdRandall: adler32 gerçekten rahatsız etmeye değmez, örneğin. leviathansecurity.com/blog/analysis-of-adler32
MikeW

6

Python 3.8+ ile şunları yapabilirsiniz

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

hashlib.blake2bBunun yerine kullanmayı düşünün md5( yukarıdaki pasaj md5ile değiştirin blake2b). Kriptografik olarak güvenli ve MD5'ten daha hızlı .


:=Operatörü (Python 3.8+ yeni) bir "atama operatörü" dir; daha büyük bir ifadenin içine değerler atamanıza olanak tanır; daha fazla bilgi için burada: docs.python.org/3/whatsnew/3.8.html#assignment-expressions
Benjamin

0
hashlib.md5(pathlib.Path('path/to/file').read_bytes()).hexdigest()

3
Selam! Lütfen bunun neden soruna bir çözüm olduğu konusunda kodunuza biraz açıklama ekleyin. Dahası, bu yazı oldukça eski, bu yüzden çözümünüzün neden diğerlerinin henüz ele almadığı bir şey eklediğine dair bazı bilgiler eklemelisiniz.
d_kennetz

1
Başka bir bellek verimsiz yolu
Erik Aronesty

-2

Ben invoke paketi ve md5sum ikili güvenerek alt süreç veya md5 paketinden biraz daha uygun olduğunu düşünüyorum

import invoke

def get_file_hash(path):

    return invoke.Context().run("md5sum {}".format(path), hide=True).stdout.split(" ")[0]

Bu tabii ki çağırdı ve md5sum yüklü varsayalım.


3
Eğer pathbir kullanıcı sağlanan yoldur, bu herhangi bir kullanıcı sisteminizde keyfi bash komutları yürütmek izin verir.
Boris
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.