किसी फ़ाइल का MD5 चेकसम जनरेट करना


348

क्या पायथन में फ़ाइलों की सूची का एमडी 5 चेकसम उत्पन्न करने (और जांचने) का कोई सरल तरीका है? (मेरे पास एक छोटा सा कार्यक्रम है जिस पर मैं काम कर रहा हूं, और मैं फाइलों के चेकसम की पुष्टि करना चाहता हूं)।


3
सिर्फ उपयोग क्यों नहीं md5sum?
kennytm

99
इसे पायथन में रखने से क्रॉस-प्लेटफॉर्म संगतता को प्रबंधित करना आसान हो जाता है।
अलेक्जेंडर

यदि आप "प्रोग्रेस बार * या इसी तरह की (बहुत बड़ी फ़ाइलों के लिए) के साथ डे सोल्यूशन चाहते हैं, तो इस समाधान पर विचार करें: stackoverflow.com/questions/1131220/…
लॉरेंट लैपर्ट

1
@kennytm आपके द्वारा दिया गया लिंक दूसरे पैराग्राफ में यह कहता है: "वर्णन करते समय अंतर्निहित एमडी 5 एल्गोरिथ्म अब सुरक्षित नहीं माना जाता है" md5sum। इसीलिए सुरक्षा-सचेत प्रोग्रामर को मेरी राय में इसका इस्तेमाल नहीं करना चाहिए।
डिबग 255

1
@ Debug255 अच्छा और मान्य बिंदु। दोनों md5sumऔर इस SO प्रश्न में वर्णित तकनीक से बचा जाना चाहिए - यदि संभव हो तो SHA-2 या SHA-3 का उपयोग करना बेहतर है: en.wikipedia.org/wiki/Secure_Hash_Al एल्गोरिदम
प्रति लंड

जवाबों:


463

आप hashlib.md5 () का उपयोग कर सकते हैं

ध्यान दें कि कभी-कभी आप पूरी फ़ाइल को मेमोरी में फिट नहीं कर पाएंगे। उस स्थिति में, आपको क्रमिक रूप से 4096 बाइट्स का हिस्सा पढ़ना होगा और उन्हें md5विधि को खिलाना होगा :

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()

नोट: यदि आप सिर्फ पैक बाइट्स उपयोग की जरूरत है, तो पाचन के लिए हेक्स स्ट्रिंग प्रतिनिधित्व hash_md5.hexdigest()लौटाएगा , इसलिए आपको वापस कन्वर्ट करने की आवश्यकता नहीं है।return hash_md5.digest()


297

वहाँ एक तरीका है कि सुंदर स्मृति अक्षम है

एक दस्तावेज:

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

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

फाइलों की सूची:

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

स्मरण करो कि एमडी 5 टूटा हुआ है और किसी भी उद्देश्य के लिए उपयोग नहीं किया जाना चाहिए क्योंकि भेद्यता विश्लेषण वास्तव में मुश्किल हो सकता है, और भविष्य में किसी भी संभावित उपयोग का विश्लेषण करना जो आपके कोड को सुरक्षा मुद्दों के लिए रखा जा सकता है असंभव है। IMHO, यह फ्लैट से हटा दिया जाना चाहिए पुस्तकालय ताकि हर कोई जो इसका उपयोग करता है वह अपडेट करने के लिए मजबूर हो। तो, यहां आपको इसके बजाय क्या करना चाहिए:

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

यदि आप केवल पचाने योग्य 128 बिट्स चाहते हैं तो आप कर सकते हैं .digest()[:16]

यह आपको टुपल्स की एक सूची देगा, प्रत्येक टपल जिसमें इसकी फ़ाइल और उसके हैश का नाम होगा।

फिर से मैं एमडी 5 के आपके उपयोग पर दृढ़ता से सवाल उठाता हूं। आपको कम से कम SHA1 का उपयोग करना चाहिए, और SHA1 में खोजी गई हाल की खामियों को देखते हुए , शायद यह भी नहीं है। कुछ लोग सोचते हैं कि जब तक आप 'क्रिप्टोग्राफिक' उद्देश्यों के लिए एमडी 5 का उपयोग नहीं कर रहे हैं, तब तक आप ठीक हैं। लेकिन सामान में शुरुआत में आपकी अपेक्षा से अधिक व्यापक होने की प्रवृत्ति होती है, और आपके आकस्मिक भेद्यता विश्लेषण पूरी तरह से गलत साबित हो सकते हैं। गेट से सही एल्गोरिदम का उपयोग करने की आदत डालना सबसे अच्छा है। यह सिर्फ अक्षरों का एक अलग गुच्छा टाइप कर रहा है। यह इतना मुश्किल नही है।

यहाँ एक तरीका है जो अधिक जटिल है, लेकिन स्मृति कुशल है :

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]

और, फिर से, चूंकि एमडी 5 टूट गया है और वास्तव में कभी भी इसका उपयोग नहीं किया जाना चाहिए:

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

फिर, आप [:16]कॉल के बाद रख सकते हैं hash_bytestr_iter(...)यदि आप केवल 128 बिट्स को पचाने के लायक चाहते हैं।


66
मैं केवल पुष्टि करने के लिए MD5 का उपयोग कर रहा हूँ कि फ़ाइल दूषित नहीं है। मैं इसे तोड़े जाने के बारे में चिंतित नहीं हूं।
अलेक्जेंडर

87
@ TheLifelessOne: और @Onnifarious डरावना चेतावनी के बावजूद, यह पूरी तरह से MD5 का अच्छा उपयोग है।
राष्ट्रपति जेम्स के। पोल्

22
। नहीं, मैं अपनी डरावनी चेतावनी के साथ खड़ा हूं। मुझे लगता है कि एमडी 5 को हटा दिया जाना चाहिए या डेप्रिसिएशन चेतावनियों के साथ आना चाहिए।
सर्वव्यापी

10
मैं शायद .dxest () के बजाय .xxigest () का उपयोग करूंगा - मनुष्य के लिए पढ़ना आसान है - जो कि ओपी का उद्देश्य है।
zbstof

21
मैं इस समाधान का इस्तेमाल किया है, लेकिन यह दो अलग pdf फ़ाइलों के लिए एक ही हैश दिया है। इसका समाधान बाइनरी मोड को निर्दिष्ट करके फाइलों को खोलना था, जो है: [(fname, hashlib.md5 (खुला (fname, 'rb' )) .read ())। हेक्सेडीगस्ट ()) fnamelst में fname के लिए] यह अधिक संबंधित है। md5 की तुलना में खुला कार्य करने के लिए, लेकिन मैंने सोचा कि यह रिपोर्ट करने के लिए उपयोगी हो सकता है कि इसे ऊपर दिए गए क्रॉस-प्लेटफॉर्म संगतता के लिए आवश्यकता बताई गई है (यह भी देखें: docs.python.org/2/tutorial/… )।
ब्लूकोड

34

मैं स्पष्ट रूप से मौलिक रूप से कुछ भी नया नहीं जोड़ रहा हूं, लेकिन इससे पहले कि मैं टिप्पणी करने की स्थिति में था, मैंने इस उत्तर को जोड़ दिया, साथ ही कोड क्षेत्र चीजों को और अधिक स्पष्ट करते हैं - वैसे भी, विशेष रूप से ओमनीफेरियस के उत्तर से @ निमो के प्रश्न का उत्तर देने के लिए:

मैं जाँच-पड़ताल के बारे में थोड़ा सोचने लगा था (विशेष रूप से ब्लॉक आकार पर सुझाव की तलाश में यहाँ आया था), और पाया है कि यह विधि आपकी अपेक्षा से अधिक तेज़ हो सकती है। सबसे तेज़ (लेकिन बहुत विशिष्ट) ले रहा है timeit.timeitया /usr/bin/timeलगभग एक फ़ाइल की जांच के कई तरीकों में से परिणाम है। 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

तो, पायथन और / usr / bin / md5sum दोनों एक 11MB फ़ाइल के लिए लगभग 30ms लगते हैं। प्रासंगिक md5sumफ़ंक्शन ( md5sum_readउपरोक्त सूची में) सर्वव्यापी के समान है:

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()

दी गई, ये एकल रन से हैं ( mmapकम से कम कुछ दर्जन रन बनने पर हमेशा तेज़ होते हैं), और मेरा आमतौर पर f.read(blocksize)बफर समाप्त होने के बाद एक अतिरिक्त मिलता है, लेकिन यह उचित रूप से दोहराने योग्य है और दिखाता है कि md5sumकमांड लाइन पर जरूरी नहीं कि पायथन इम्प्लीमेंटेशन से ज्यादा तेज ...

संपादित करें: लंबे समय तक देरी के लिए क्षमा करें, कुछ समय में इस पर ध्यान नहीं दिया गया, लेकिन @ EdRandall के प्रश्न का उत्तर देने के लिए, मैं एक Adler32 कार्यान्वयन लिखूंगा। हालाँकि, मैंने इसके लिए बेंचमार्क नहीं चलाया है। यह मूल रूप से CRC32 के समान है: init के बजाय, अपडेट, और कॉल को हजम करना, सब कुछ zlib.adler32()कॉल है:

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

ध्यान दें कि यह खाली स्ट्रिंग के साथ शुरू होना चाहिए, क्योंकि एडलर रकम शून्य से शुरू होने के दौरान वास्तव में भिन्न होती है "", जो कि इसके लिए राशि है 1- सीआरसी 0इसके बजाय शुरू हो सकता है। AND-Ing यह एक 32-बिट अहस्ताक्षरित पूर्णांक है, जो यह अजगर संस्करणों में एक ही मान देता है सुनिश्चित करता है बनाने के लिए की जरूरत है।


क्या आप संभवतः SHA1 की तुलना करने वाली कुछ पंक्तियों को जोड़ सकते हैं, और शायद zlib.adler32 भी?
एड रान्डेल

1
Md5sum () फ़ंक्शन ऊपर माना जाता है कि आपके पास फ़ाइल तक पहुंच है। यदि आप खुले में "आर + बी" की जगह लेते हैं () "आरबी" के साथ कॉल करें तो यह ठीक काम करेगा।
केविन Lyda

1
@EdRandall: adler32 वास्तव में परेशान करने लायक नहीं है, जैसे। leviathansecurity.com/blog/analysis-of-adler32
माइकडब्ल्यू

6

पायथन 3.8+ में आप कर सकते हैं

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.blake2bकरने के बजाय md5(बस की जगह md5के साथ blake2bऊपर स्निपेट में)। यह क्रिप्टोग्राफिक रूप से सुरक्षित और एमडी 5 से तेज है।


:=ऑपरेटर एक "असाइनमेंट ऑपरेटर" (अजगर 3.8 + के लिए नया) है; यह आपको एक बड़ी अभिव्यक्ति के अंदर मान निर्दिष्ट करने की अनुमति देता है; यहाँ अधिक जानकारी: docs.python.org/3/whatsnew/3.8.html#assignment-expressions
बेंजामिन

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

3
नमस्ते! कृपया अपने कोड में कुछ स्पष्टीकरण जोड़ें क्योंकि यह समस्या का समाधान क्यों है। इसके अलावा, यह पद बहुत पुराना है, इसलिए आपको कुछ जानकारी भी जोड़नी चाहिए क्योंकि आपका समाधान कुछ ऐसा क्यों जोड़ता है जो दूसरों को पहले से ही संबोधित नहीं किया गया है।
d_kennetz

1
यह एक और स्मृति अक्षम्य तरीका है
Erik Aronesty

-2

मुझे लगता है कि इनवोक पैकेज और md5sum बाइनरी पर निर्भर होना, सबप्रोसेस या md5 पैकेज की तुलना में थोड़ा अधिक सुविधाजनक है

import invoke

def get_file_hash(path):

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

यह निश्चित रूप से माना जाता है कि आपने इनवोक और md5sum स्थापित किया है।


3
यदि pathकोई उपयोगकर्ता प्रदान किया गया मार्ग है, तो यह किसी भी उपयोगकर्ता को आपके सिस्टम पर मनमाने ढंग से कमांड को निष्पादित करने देगा।
बोरिस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.