Python SHA-256 文件校验:算对摘要之后,还要确认校验值从哪里来

10-01 5阅读

下载文件后算出一串 SHA-256,只完成了测量。真正有用的下一步,是拿它与一个明确来源的预期值比较。若预期值也是从刚下载的文件重新算出来,两边当然一致,却没有回答“这是不是我要的那个发布包”。开始之前,先写清版本、平台、文件名和校验值来源。

Python SHA-256 文件校验:算对摘要之后,还要确认校验值从哪里来

AI生成概念示意图,非真实界面

先用固定样本建立可验证基准

下面只创建本地临时文件,不下载或执行安装包。样本内容固定为两行配置,EXPECTED 是该样本预先计算好的摘要,作为教学基准。实际校验软件时,应替换成发布方针对目标文件提供、且来源已经核实的值,不能沿用示例常量,也不能现场拿待验文件生成基准。

Python hashlib 接收字节,连续 update 的结果等价于按顺序拼接字节后计算。示例以二进制模式读文件,每次只读一小块,方便观察流程;正式处理大文件可增加块大小以减少读取次数。块大小改变不应改变摘要,代码专门比较了单字节和较大块的结果。

运行等长改动与替换清单实验

把代码保存为 sha256-demo.py,在可写测试目录执行 python sha256-demo.py。临时文件位于当前目录下的独立子目录,运行结束自动清理。代码先验证原内容,再只改版本数字,最后模拟一个随文件一起被替换的校验值;三个阶段共用同一种算法。

import hashlib
from pathlib import Path
from tempfile import TemporaryDirectory

EXPECTED = '4abb1f231ad9893f8974c78d31d5e41f80c45cb1eb382852f835c1f9211385d0'

def digest_file(path, chunk_size=8):
    if chunk_size <= 0:
        raise ValueError('chunk_size must be positive')
    digest = hashlib.sha256()
    with path.open('rb') as stream:
        for chunk in iter(lambda: stream.read(chunk_size), b''):
            digest.update(chunk)
    return digest.hexdigest()

with TemporaryDirectory(prefix='sha-demo-', dir='.') as directory:
    path = Path(directory) / 'release.txt'
    original = b'release=1\nmode=demo\n'
    path.write_bytes(original)
    initial = digest_file(path)
    assert initial == EXPECTED
    assert digest_file(path, 1) == digest_file(path, 1024)
    print('original matches:', initial == EXPECTED)
    print('sha256:', initial)

    changed = b'release=2\nmode=demo\n'
    path.write_bytes(changed)
    actual = digest_file(path)
    assert len(changed) == len(original)
    assert actual != EXPECTED
    print('same byte length:', len(changed) == len(original))
    print('changed matches trusted baseline:', actual == EXPECTED)

    untrusted_checksum = hashlib.sha256(changed).hexdigest()
    assert actual == untrusted_checksum
    print('changed matches replaced checksum:', actual == untrusted_checksum)

摘要相同究竟说明了什么

输出应依次出现 original matches: True、原摘要、same byte length: True、changed matches trusted baseline: False、changed matches replaced checksum: True。中间两项说明,文件大小相同仍可能内容不同;末项则说明,被改过的文件也能与攻击者重新生成的校验值完全匹配。

后一种情况没有破解 SHA-256,问题在于比较基准也被换掉了。可以把校验记录设计成一组证据:文件名、发布版本、实际摘要、预期摘要、基准页面地址和核验时间。审核者据此能判断比较了哪两个对象,而不是只看到一个没有上下文的“校验通过”。

Apache 的发布校验指南明确区分哈希检查与真实性验证,并强调签名公钥的真实性也要核实。因此,对发布包可继续按项目官方流程验证签名、发布证明或受信任清单。看到“签名有效”后仍要核对对应发布者身份,避免把任意一把公钥当成发布方的公钥。

排错时固定字节对象与检查时机

校验失败先暂停使用该文件,再核对版本与文件类型:压缩包、解压后的程序以及网页错误响应是不同字节对象。不要为了让数值相等而修改预期摘要。还应查看下载是否完成、目标路径是否选错,以及发布方是否为另一平台或另一构建提供了清单。

文件摘要应直接读取原始字节。先按文本打开,再统一换行或重新编码,测量对象已经变化;肉眼看起来同样的两行文字也可能有不同字节。同理,摘要一致无法证明程序没有漏洞,也无法证明压缩包内容符合你的业务需求,这些要由另外的检查回答。

手动检查可把样本最后一个换行删掉,确认摘要发生变化;把块大小换成三、十七或一千,确认结果保持一致。用于真实流程时,还应确保校验期间文件不会被并发改写,并让后续使用的正是已校验对象。否则校验通过后文件再被替换,先前记录不能为新内容作证。

参考资料

文章版权声明:除非注明,否则均为云鹊BLOG原创文章,转载或复制请以超链接形式并注明出处。