파일의 MD5 체크섬 생성


348

파이썬에서 파일 목록의 MD5 체크섬을 생성하고 확인하는 간단한 방법이 있습니까? (작업중 인 작은 프로그램이 있으며 파일의 체크섬을 확인하고 싶습니다).


3
왜 사용하지 md5sum않습니까?
kennytm

99
파이썬으로 유지하면 크로스 플랫폼 호환성을보다 쉽게 ​​관리 할 수 ​​있습니다.
Alexander

: 당신이 "진행 막대 * 또는 (매우 큰 파일의 경우) 유사과 드 솔루션을 원하는 경우,이 솔루션을 고려 stackoverflow.com/questions/1131220/...
로랑 LAPORTE

1
@kennytm 두 번째 단락에서 제공 한 링크에서이를 설명 md5sum합니다. "기본 MD5 알고리즘은 더 이상 안전하지 않습니다 . " 이것이 보안에 민감한 프로그래머가 제 생각에 사용해서는 안되는 이유입니다.
Debug255

1
@ Debug255 좋은 점. md5sum이 SO 질문에 설명 된 기술과 기술은 모두 피해야합니다. 가능하면 SHA-2 또는 SHA-3을 사용하는 것이 좋습니다. en.wikipedia.org/wiki/Secure_Hash_Algorithms
Per Lundberg

답변:


464

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

참고 : 압축 된 바이트 use 만 필요하면 다이제스트에 대한 16 진수 문자열 표현을 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]

그러나 MD5는 고장난 것으로 알려져 있으며 취약성 분석이 실제로 까다로울 수 있기 때문에 어떤 목적으로도 사용해서는 안된다는 점을 기억 하십시오. 향후 발생할 수있는 사용을 분석하는 것은 보안 문제로 인해 불가능할 수 있습니다. IMHO, 라이브러리에서 평평하게 제거해야 라이브러리를 사용하는 모든 사람이 강제로 업데이트됩니다. 따라서 대신 수행해야 할 작업은 다음과 같습니다.

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

128 비트의 다이제스트 만 원한다면 할 수 있습니다 .digest()[:16].

그러면 파일 이름과 해시가 들어있는 튜플 목록이 나타납니다.

다시 한 번 MD5 사용에 의문을 제기합니다. 최소한 SHA1을 사용해야 하고 SHA1 에서 발견 된 최근 결함이 있을 수 있습니다. 어떤 사람들은 MD5를 '암호화'목적으로 사용하지 않는 한 괜찮다고 생각합니다. 그러나 물건은 처음에 예상했던 것보다 범위가 넓어지는 경향이 있으며 일시적인 취약점 분석에 완전히 결함이있을 수 있습니다. 게이트에서 올바른 알고리즘을 사용하는 습관을들이는 것이 가장 좋습니다. 그냥 다른 문자를 입력하는 것입니다. 그렇게 어렵지 않습니다.

더 복잡하지만 메모리 효율적인 방법은 다음과 같습니다 .

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]

그리고 다시, MD5가 손상되어 더 이상 사용해서는 안됩니다.

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

다시 말하지만 128 비트의 다이제스트 만 원한다면 [:16]호출 후에 넣을 수 있습니다 hash_bytestr_iter(...).


66
파일이 손상되지 않았는지 확인하기 위해 MD5 만 사용하고 있습니다. 나는 그것이 깨지는 것에 대해 걱정하지 않습니다.
Alexander

87
@TheLifelessOne : 그리고 @Omnifarious 무서운 경고에도 불구하고, 그것은 MD5를 완벽하게 잘 사용합니다.
James K. Polk 대통령

22
@GregS, @TheLifelessOne-예, 그리고 다음으로 아는 사람은 응용 프로그램에 대해이 사실을 사용하여 파일이 예상 한 파일이 아닐 때 파일이 손상되지 않은 것으로 받아 들여질 수있는 방법을 찾습니다. 아니요, 나는 무서운 경고를 기다립니다. MD5를 제거하거나 사용 중단 경고가 표시되어야한다고 생각합니다.
Omnifarious

10
아마 .digest () 대신 .hexdigest ()를 사용했을 것입니다-인간이 읽기가 더 쉽습니다-이것이 OP의 목적입니다.
zbstof

21
이 솔루션을 사용했지만 두 개의 다른 pdf 파일에 대해 동일한 해시를 잘못 제공했습니다. 해결책은 이진 모드를 지정하여 파일을 여는 것입니다. 즉 [fnamelst의 fname에 대한 [(fname, hashlib.md5 (open (fname, 'rb' ) .read ()). hexdigest ())] 더 관련이 있습니다. md5보다 개방 함수에 있지만 위에서 언급 한 크로스 플랫폼 호환성 요구 사항을 감안하여보고하는 것이 유용 할 것이라고 생각했습니다 ( docs.python.org/2/tutorial/… 참조 ).
BlueCoder

34

나는 근본적으로 새로운 것을 추가하지는 않지만 상태를 주석 처리하기 전에이 답변을 추가했으며 코드 영역은 상황을보다 명확하게 만듭니다. 어쨌든 Omnifarious의 답변에서 @Nemo의 질문에 대답합니다.

나는 체크섬에 대해 조금 생각하고 있었고 (특히 블록 크기에 대한 제안을 찾으러 왔음)이 방법이 예상보다 빠르다는 것을 알았습니다. 파일을 대략적으로 검사하는 여러 가지 방법 을 사용하여 가장 빠르 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은 Omnifarious의 기능과 매우 유사합니다.

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, update 및 digest 호출 대신 모든 것이 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

Adler 합계는 0에서 시작하는 경우의 합계가 실제로 다르기 때문에 빈 문자열로 시작해야합니다. 즉 "", 1CRC는 0대신 시작할 수 있습니다 . AND-ing은이 파이썬 버전에서 동일한 값을 반환 보장하는 32 비트 부호없는 정수 만들 필요합니다.


SHA1과 zlib.adler32를 비교하는 몇 줄을 추가 할 수 있습니까?
Ed Randall

1
위의 md5sum () 함수는 파일에 대한 쓰기 액세스 권한이 있다고 가정합니다. open () 호출에서 "r + b"를 "rb"로 바꾸면 정상적으로 작동합니다.
Kevin Lyda

1
@ EdRandall : adler32는 실제로 귀찮게 할 가치가 없습니다. leviathansecurity.com/blog/analysis-of-adler32
MikeW

6

Python 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위의 미리보기에서). 암호화 적으로 안전하고 MD5보다 빠릅니다 .


:=연산자 (파이썬 3.8+ 새로운)는 "대입 연산자"입니다; 더 큰 표현식 안에 값을 할당 할 수 있습니다. 자세한 정보는 여기에 있습니다 : docs.python.org/3/whatsnew/3.8.html#assignment-expressions
Benjamin

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사용자가 제공 한 경로 인 경우 모든 사용자가 시스템에서 임의의 bash 명령을 실행할 수 있습니다.
Boris
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.