1. 使用哈希函数生成文件完整性校验值

使用哈希函数生成文件完整性校验值 学习卡片

任务目标与核心思路

文件完整性校验的核心是:对文件内容逐字节计算 SHA-256 摘要,得到一个 256 位(32 字节)的哈希值,再与可信来源提供的预期摘要比对。若两者完全一致,说明文件内容未被篡改;若不一致,则文件已发生变化。

本案例使用 Python 标准库 hashlib 中的 sha256() 接口完成计算。关键点有三个:

  • 以二进制模式读取文件,避免文本编码转换改变字节内容。
  • 对大文件采用分块读取(如每次 64 KiB),避免一次性载入内存。
  • 比对时使用 hmac.compare_digest() 进行恒定时间比较,避免时序侧信道。

SHA-256 文件摘要计算与比对流程

目标文件 data.bin 二进制模式读取 分块读取 chunk = 64 KiB 循环 update() SHA-256 哈希引擎 hashlib.sha256() 256 位 / 32 字节 输出十六进制 64 字符 计算摘要 digest = hexdigest() 预期摘要 来自可信来源 恒定时间比对 hmac.compare_digest(实际, 预期) ✓ 一致 文件未被篡改 ✗ 不一致 文件已变化或被篡改 SHA-256 摘要长度固定为 64 个十六进制字符,任何 1 bit 内容变化都会导致摘要完全不同

完整实现代码

以下代码可直接运行,包含文件摘要计算、预期值比对和结果输出。示例中先创建一个测试文件,再分别演示正确比对和篡改后比对失败两种情况。

import hashlib
import hmac
import os

def sha256_file(filepath, chunk_size=65536):
    """分块计算文件的 SHA-256 摘要,返回十六进制字符串。"""
    h = hashlib.sha256()
    with open(filepath, "rb") as f:
        while True:
            chunk = f.read(chunk_size)
            if not chunk:
                break
            h.update(chunk)
    return h.hexdigest()

def verify_file(filepath, expected_hex):
    """计算文件摘要并与预期值比对,返回 (是否一致, 实际摘要)。"""
    actual = sha256_file(filepath)
    match = hmac.compare_digest(actual, expected_hex.lower())
    return match, actual

# ---- 教学示例:创建测试文件 ----
test_path = "demo_integrity.bin"
with open(test_path, "wb") as f:
    f.write(b"Password API Practice 100 - Case 01\n")
    f.write(os.urandom(1024))  # 追加 1 KiB 随机字节

# 1) 计算并打印摘要
digest = sha256_file(test_path)
print("实际摘要:", digest)

# 2) 用正确预期值比对
ok, actual = verify_file(test_path, digest)
print("正确预期值比对:", "通过" if ok else "失败")

# 3) 模拟篡改:修改文件内容后再比对
with open(test_path, "ab") as f:
    f.write(b"TAMPERED")

ok2, actual2 = verify_file(test_path, digest)
print("篡改后比对:", "通过" if ok2 else "失败")
print("篡改后摘要:", actual2)

# 清理
os.remove(test_path)

运行结果与判读

运行上述代码,预期输出如下(摘要值因随机字节不同而不同,但结构一致):

实际摘要: 3a7f...(64 个十六进制字符)
正确预期值比对: 通过
篡改后比对: 失败
篡改后摘要: 9c2e...(与原始摘要完全不同)

判读要点:

  • 正确预期值比对通过:说明计算路径正确,文件内容与预期摘要匹配。
  • 篡改后比对失败:追加 8 字节后摘要完全改变,验证了 SHA-256 的雪崩效应——输入任何微小变化都会导致输出摘要不可预测地大幅变化。
  • 摘要长度固定:无论文件多大,SHA-256 输出始终为 64 个十六进制字符(256 位)。
实践建议:实际校验文件时,预期摘要应来自独立可信渠道(如官方发布页、签名文件、内部配置管理系统),而不是与文件放在同一位置。若预期摘要本身可被篡改,完整性校验就失去了意义。
常见误区:不要用 == 直接比较摘要字符串。虽然在本案例的教学场景中风险极低,但在安全敏感场景中,字符串比较可能因提前返回而泄露匹配前缀长度信息。使用 hmac.compare_digest() 是更稳妥的习惯。

关键参数与验证清单

项目说明本案例取值
哈希算法密码学哈希函数SHA-256
读取模式必须二进制读取"rb"
分块大小平衡内存与 I/O 次数65536 字节(64 KiB)
摘要输出格式十六进制字符串64 字符
比对函数恒定时间比较hmac.compare_digest()
通过条件实际摘要 == 预期摘要完全一致
失败条件实际摘要 != 预期摘要任何差异