1. 使用哈希函数生成文件完整性校验值
任务目标与核心思路
文件完整性校验的核心是:对文件内容逐字节计算 SHA-256 摘要,得到一个 256 位(32 字节)的哈希值,再与可信来源提供的预期摘要比对。若两者完全一致,说明文件内容未被篡改;若不一致,则文件已发生变化。
本案例使用 Python 标准库 hashlib 中的 sha256() 接口完成计算。关键点有三个:
- 以二进制模式读取文件,避免文本编码转换改变字节内容。
- 对大文件采用分块读取(如每次 64 KiB),避免一次性载入内存。
- 比对时使用
hmac.compare_digest()进行恒定时间比较,避免时序侧信道。
SHA-256 文件摘要计算与比对流程
完整实现代码
以下代码可直接运行,包含文件摘要计算、预期值比对和结果输出。示例中先创建一个测试文件,再分别演示正确比对和篡改后比对失败两种情况。
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() |
| 通过条件 | 实际摘要 == 预期摘要 | 完全一致 |
| 失败条件 | 实际摘要 != 预期摘要 | 任何差异 |