Python中的文件校验和

Python中的文件校验和,python,django,file,checksum,Python,Django,File,Checksum,我正在创建一个与文件相关的应用程序。我在寻找计算文件校验和的方法。我想知道根据这个标准计算md5或SHA-1或其他文件校验和的最佳哈希方法是什么 校验和应该是唯一的。我知道这是理论上的,但我还是希望碰撞的概率非常小 如果校验和是否相等,则可以比较两个文件是否相等 速度(不是很重要,但仍然很重要) 请尽可能详细地解释 几天前,我创建了一个小的重复文件删除脚本,它读取文件的内容并为其创建一个哈希,然后与下一个文件进行比较,在该脚本中,即使名称不同,校验和也将相同 import hashlib i

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

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

请尽可能详细地解释

几天前,我创建了一个小的重复文件删除脚本,它读取文件的内容并为其创建一个哈希,然后与下一个文件进行比较,在该脚本中,即使名称不同,校验和也将相同

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    

md5往往对校验和非常有用。。。和SHA-1一样。。。虽然我认为SHA-1的碰撞概率稍小,因为它使用了更多的比特,但两者的碰撞概率都很小

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

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


除了逐字节比较之外,没有办法100%保证唯一性。这取决于您的用例

如果您只担心意外碰撞,MD5和SHA-1都可以,而且MD5通常更快。事实上,MD4对于大多数用例来说也是足够的,而且通常更快……但是它并没有得到广泛的实现。(特别是,它不在
hashlib.algorithms\u保证中
…尽管它应该在大多数Mac、Windows和Linux版本上可用的
hashlib\u算法中。)

另一方面,如果你担心故意攻击,即某人故意伪造与你的散列匹配的假文件,你必须考虑你所保护的东西的价值。MD4几乎肯定是不够的,MD5可能是不够的,但SHA-1处于临界状态。目前,Keccak(SHA-3即将推出)被认为是最好的选择,但你会希望保持领先,因为情况每年都在变化

上的Wikipedia页面有一个表,通常更新得相当频繁。要了解该表,请执行以下操作:

与MD4产生碰撞只需要3发子弹,而MD5需要大约200万发子弹,而SHA-1需要15万亿发子弹。这就足够了,以今天的价格计算,制造一次碰撞将花费数百万美元。这对你来说可能不够好,也可能不够好,但对NIST来说还不够好


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

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')
,并使用熵较小的
字节
(以空格分隔的1-8个
string.ascii_字母运行)的测试没有显示出任何显著差异

而且,对于我的安装附带的散列算法,正如下面测试的,没有什么比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
尝试不同的值(从4KB到8MB的二次幂)。您可能还想尝试使用原始文件句柄(
os.open
os.read
),这在某些平台上可能会更快。

哈希大小足够位的冲突可能性非常小:

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

到目前为止,还没有观测到160位的SHA-1碰撞。假设8KB块中有一个EB(10^18)的数据,理论上发生冲突的可能性是10^-20——这是一个非常小的可能性

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

例如,在大纲中:

  • 读取所有感兴趣文件的前X个块
  • 将前X个块具有相同哈希的块排序为可能相同的文件数据
  • 对于具有唯一前X块的每个文件,您可以假设整个文件相对于所有其他测试文件都是唯一的——您不需要读取该文件的其余部分
  • 对于剩余的文件,读取更多块,直到证明签名相同或不同
    有了足够大的X块,95%以上的文件将在第一次通过时被正确识别为唯一的文件。这比盲目读取整个文件并计算每个文件的完整哈希要快得多

    md5往往对校验和非常有用。。。sa