Python中的文件校验和

时间:2013-05-28 18:37:47

标签: python django file checksum

我正在创建与文件相关的应用程序。我一直在寻找计算文件校验和的方法。我想知道根据这个标准计算文件md5或SHA-1或其他东西的校验和的最佳散列方法是什么

  • 校验和应该是唯一的。我知道它的理论,但我仍然希望碰撞的可能性非常小。
  • 如果校验和相等,则可以将两个文件进行比较。
  • 速度(不是很重要,但仍然)

请尽可能精心准备。

4 个答案:

答案 0 :(得分:5)

这取决于您的使用案例。

如果您只担心意外碰撞,MD5和SHA-1都可以,MD5通常更快。实际上,MD4对于大多数用例来说也足够了,通常甚至更快......但它并没有广泛实现。 (特别是,它不在hashlib.algorithms_guaranteed ...虽然它应该在大多数Mac,Windows和Linux版本的hashlib_algorithms_available中。)

另一方面,如果你担心故意攻击 - 即有人故意制作一个与你的哈希相匹配的伪造文件 - 你必须考虑你所保护的价值。 MD4几乎绝对不够,MD5可能还不够,但SHA-1是临界的。目前,Keccak(很快将由SHA-3推出)被认为是最好的选择,但是你会希望保持最佳状态,因为每年都会发生变化。

Cryptographic hash function上的维基百科页面有一个通常经常更新的表格。要理解该表:

要生成与MD4的碰撞只需要3轮,而MD5需要大约200万,而SHA-1需要15万亿。这足以导致数百万美元(以今天的价格)产生碰撞。这可能对您不够好,但对NIST来说还不够好。


另外,请记住“通常更快”并不像“在我的数据和平台上测试得更快”那么重要。考虑到这一点,在Mac上的64位Python 3.3.0中,我创建了一个1MB的随机bytes对象,然后执行了此操作:

In [173]: md4 = hashlib.new('md4')
In [174]: md5 = hashlib.new('md5')
In [175]: sha1 = hashlib.new('sha1')
In [180]: %timeit md4.update(data)
1000 loops, best of 3: 1.54 ms per loop
In [181]: %timeit md5.update(data)
100 loops, best of 3: 2.52 ms per loop
In [182]: %timeit sha1.update(data)
100 loops, best of 3: 2.94 ms per loop

如您所见,md4明显快于其他人。

使用hashlib.md5()代替hashlib.new('md5')进行测试,并使用bytes使用较少的熵(1-8 string.ascii_letters以空格分隔)并未显示任何显着差异。

而且,对于我的安装附带的哈希算法,如下面所测试的,没有什么比md4更好。

for x in hashlib.algorithms_available:
    h = hashlib.new(x)
    print(x, timeit.timeit(lambda: h.update(data), number=100))

如果速度非常重要,可以使用一个很好的技巧来改进:使用一个糟糕但非常快速的哈希函数,如zlib.adler32,并且只将它应用于每个文件的前256KB 。 (对于某些文件类型,最后256KB,或最接近中间的256KB而不会过去,等等可能比第一个更好。)然后,如果发现碰撞,生成MD4 / SHA-1 / Keccak /任何哈希值每个文件的整个文件。


最后,因为有人在评论中询问如何散写文件而不将整个内容读入内存:

def hash_file(path, algorithm='md5', bufsize=8192):
    h = hashlib.new(algorithm)
    with open(path, 'rb') as f:
        block = f.read(bufsize)
        if not block:
            break
        h.update(block)
    return h.digest()

如果挤出每一点性能都很重要,那么您需要在平台上尝试bufsize的不同值(2的幂从4KB到8MB)。您可能还想尝试使用原始文件句柄(os.openos.read),这在某些平台上有时会更快。

答案 1 :(得分:2)

具有足够位的散列大小的碰撞可能性theoretically非常小:

  

假设具有均匀分布的随机散列值,即集合   n个不同的数据块和生成b位的散列函数,   将存在一个或多个碰撞的概率p是有界的   通过块对的数量乘以a的概率   给对将发生碰撞,即

enter image description here

到目前为止,尚未观察到具有160位的SHA-1冲突。假设一个exabyte(10 ^ 18)的数据,在8KB块中,碰撞的理论机会是10 ^ -20 - 非常小的机会。

一个有用的捷径是通过短路消除已知彼此不同的文件。

例如,概要:

  1. 阅读所有感兴趣文件的前X个块;
  2. 将前X个块具有相同散列的那个排序为可能相同的文件数据;
  3. 对于每个具有唯一X块的文件,您可以假设整个文件与所有其他测试文件相比是唯一的 - 您无需阅读该文件的其余部分;
  4. 使用其余文件,阅读更多块,直到您证明签名相同或不同为止。
  5. 对于足够大小的X块,在第一遍中,95%以上的文件将被正确区分为唯一文件。这比盲目地读取整个文件并计算每个文件的完整哈希要快得多。

答案 2 :(得分:1)

md5对于校验和来说效果很好......与SHA-1相同......两者的碰撞概率都非常小,但我认为SHA-1的碰撞概率略小,因为它使用了更多的位

如果你真的很担心它,你可以使用两个校验和(一个md5和一个sha1)匹配和文件差异的机会极小(仍然不是100%不可能但非常非常不可能)...... (这似乎是糟糕的形式,也是迄今为止最慢的解决方案)

通常(读取:在我遇到的每个实例中)MD5或SHA1匹配足以假设唯一性

没有办法100%保证每个字节比较的唯一性

答案 3 :(得分:0)

几天前我创建了一个小的重复文件移除脚本,它读取文件的内容并为其创建一个哈希值,然后与下一个文件进行比较,即使名称不同,校验和将会是同样..

import hashlib
import os

hash_table = {}
dups = []
path = "C:\\images"
for img in os.path.listdir(path):
    img_path = os.path.join(path, img)
    _file = open(img_path, "rb")
    content = _file.read()
    _file.close()
    md5 = hashlib.md5(content)
    _hash = md5.hexdigest()

    if _hash in hash_table.keys():
        dups.append(img)
    else:
        hash_table[_hash] = img