哈希函数详解:简单验证文件完整性
哈希函数对传输后验证文件完整性至关重要。了解SHA-256和MD5如何确保文件无损到达。
哈希函数接受任意输入,产生固定长度的摘要——SHA-256 始终输出 256 位(32 字节),无论输入是一条短消息还是一个 10 GB 的视频。改变输入中的任意一个比特,大约一半的输出比特随之翻转。这种单向、确定性的特性使哈希成为验证文件完整性的首选工具:传输前计算 SHA-256(文件),接收后再次计算,两者一致则文件完好送达。Linux 发行版用它验证 ISO 镜像,Git 用它标识提交,HexaTransfer 等服务用它确认多吉字节上传无损完成。
哈希函数的核心保证
密码学哈希函数由三个核心属性定义:
- 确定性。 相同输入始终产生相同输出。
SHA-256("hello")恒为2cf24dba5fb0a30e...。 - 原像抗性。 给定哈希值,无法高效找到能产生该值的输入。
- 抗碰撞性。 无法高效找到两个不同输入产生相同哈希值。
另有两种实用特性:
- 雪崩效应。 改变一个输入比特,约 50% 的输出比特发生翻转。使哈希无法用于搜索,但非常适合文件指纹。
- 固定输出长度。 SHA-256 为 32 字节,SHA-512 为 64 字节,BLAKE2b 为 64 字节,与输入大小无关。
哈希不是加密。它是单向的——无法从输出还原输入。这正是其设计意图。
SHA 家族
由 NIST 在 FIPS 180-4 和 FIPS 202 中标准化的安全哈希算法家族:
- SHA-1 — 160 位,2017 年被 Google SHAttered 攻击攻破,不要用于安全场景。
- SHA-2(SHA-224、SHA-256、SHA-384、SHA-512)。 现代系统的主力,SHA-256 占主导。
- SHA-3(Keccak)。 不同构造(海绵结构而非 Merkle-Damgård),2015 年标准化,作为 SHA-2 潜在弱点的后备方案,但该弱点至今未实际出现。
SHA-256 是 TLS 证书签名、Bitcoin 挖矿和 Git 使用的算法,也是文件传输服务执行完整性校验的标准。支持 SHA 扩展指令的现代 CPU 单核吞吐量约为 2–4 GB/s。
MD5 和 SHA-1 为何在安全场景中已成过去
MD5(1991 年)产生 128 位哈希,曾是业界标准,直到 2004 年碰撞攻击被公开演示。2012 年,Flame 恶意软件利用 MD5 碰撞攻击伪造了微软代码签名证书。今天,在普通笔记本电脑上几秒钟即可生成 MD5 碰撞。
SHA-1 存活时间更长,但在 2017 年被 Google SHAttered 攻击攻破——该攻击耗费 110 GPU 年算力后产生了两个 SHA-1 哈希相同的 PDF 文件,如今在云 GPU 上花费不到 10 万美元即可复现。
两者仍适用于非安全场景:检测意外数据损坏、存储去重、缓存键指纹。但不适用于数字签名、密码验证,或攻击者可从伪造匹配中获益的任何场景。
BLAKE2 和 BLAKE3:速度与安全的兼顾
BLAKE2(2012 年)和 BLAKE3(2020 年)以 SHA-256 两到十倍的速度实现 SHA-3 级别的安全性。BLAKE3 单线程吞吐量约 6 GB/s,且在多核上线性扩展——16 核机器可达 100+ GB/s。
采用率持续增长:WireGuard 使用 BLAKE2s 进行认证,Zcash 使用 BLAKE2b,b3sum 正逐渐成为开发工具中 sha256sum 的替代品。处理多吉字节上传的文件传输服务越来越多地采用 BLAKE3,避免哈希计算成为性能瓶颈。
目前浏览器的 Web Crypto API 尚未暴露 BLAKE3,因此 JavaScript 实现依赖 WASM 编译的参考代码,在浏览器中速度约为 500 MB/s。
哈希在文件传输工作流中的作用
哈希在多个关键环节发挥价值:
- 上传后完整性校验。 客户端上传时计算 SHA-256,服务器到达时再次计算,不匹配则触发重新上传。S3 使用 MD5 完成此操作,因为速度比抗碰撞性更重要。
- 分块上传验证。 大文件分割为 4 MB 或 5 MB 的块,每块有独立哈希,哈希树(Merkle 树)为整个文件生成单一根哈希。
- 去重存储。 两个用户上传相同文件时,哈希匹配,存储只保存一份。Dropbox 的块级去重和 CDN 缓存均使用此机制。
- 下载验证。 部分服务在下载页面显示 SHA-256,供接收方自行核验。
- 版本标识。 Git 使用 SHA-1(正在迁移至 SHA-256)作为提交 ID——文件内容决定其身份标识。
HMAC 扩展:从完整性到真实性
纯哈希可以验证完整性,但无法验证真实性——任何人都可以计算 SHA-256(文件)。添加密钥将哈希转变为消息认证码(MAC):只有持有密钥的人才能生成或验证 MAC。
HMAC(RFC 2104)是标准构造:HMAC(key, msg) = SHA-256(key' ⊕ opad ‖ SHA-256(key' ⊕ ipad ‖ msg))。用于 TLS 1.2、以 HS256 签名的 JWT、AWS 请求签名以及许多会话 cookie 方案。
在文件传输中,HMAC 的角色体现在 AES-GCM 的认证标签(使用 GHASH 而非 HMAC,但作用相同)以及部分服务的 API 请求签名中。
Merkle 树:大规模数据的哈希方案
对于超大文件或文件集合,每次都对全部内容重新哈希是低效的。Merkle 树将哈希组织成二叉树结构:叶节点是块哈希,内部节点是其子节点的哈希,根哈希代表整个数据集。
主要优势:
- 高效更新。 修改一个块只需重新哈希 log(n) 个节点。
- 包含证明。 只需 log(n) 个兄弟哈希即可证明特定块属于根哈希。
- 并行计算。 各分支可独立并行哈希。
BitTorrent(v2 以后)、IPFS 内容寻址、Git 树对象、证书透明日志和区块链交易根均使用 Merkle 树。文件传输服务使用它支持断点续传,对部分已上传内容进行验证。
密码哈希与文件哈希的区别
哈希的另一个应用:将密码转换为可安全存储的摘要。简单的 SHA-256(密码) 远远不够——GPU 集群每秒可计算数十亿次 SHA-256,任何常见密码都会被瞬间破解。
专用密码哈希函数通过增加计算成本来应对:
- PBKDF2-HMAC-SHA-256:60 万次迭代(OWASP 2023 建议值)。
- bcrypt:成本因子 12(每次哈希约 250 毫秒)。
- scrypt:内存硬化,抵抗 GPU 大规模并行攻击。
- Argon2id:当前最佳实践,兼具内存硬化和抗并行攻击特性。
目标不在于哈希算法本身的安全性,而是让每次猜测成本足够高,使离线暴力破解对强度合理的密码变得不可行。
手动验证下载文件
任何需要关注的文件都可以用命令行工具进行验证。在 macOS 或 Linux 上:
shasum -a 256 ubuntu-24.04.iso
在 Windows PowerShell 上:
Get-FileHash ubuntu-24.04.iso -Algorithm SHA256
将输出与 ubuntu.com 上发布的哈希对比。匹配则文件下载完整,与 Canonical 签名的版本一致。不匹配则说明下载损坏或文件已被替换。
要获得最高信任级别,可用 GPG 和 Canonical 签名密钥验证哈希文件本身的签名。签名哈希文件→文件哈希→文件的多层链条,正是 Linux 发行版几十年来维护信任的方式。
评估文件传输服务的完整性处理能力
向文件传输服务商询问以下四个问题,值得认真对待:
- 用什么哈希算法验证上传?(SHA-256 或 BLAKE3 均可;MD5 仅适用于损坏检测;对此沉默是一个警示信号。)
- 是否向接收方显示哈希值?(并非所有服务都这样做,但这是加分项。)
- 分块上传是否进行 Merkle 树验证?
- 是否提供跨端点哈希比对的方式?
SwissTransfer、Tresorit、Proton Drive 和 HexaTransfer 均在传输过程中计算 SHA-256 并在到达时验证。完整性校验失败会触发受影响块的自动重传,而不是导致整个传输任务失败。
在 hexatransfer.com 上试试 — 免费、无需注册、最多 10 GB。