파일의 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.