本节摘要:同一条"指纹"思想在三个场景长成三种形状:文件校验只需公开比对的抗碰撞性;口令存储要求加盐慢哈希,专门对付 GPU 暴力;默克尔树把哈希串成树,把海量数据的完整性验证压到对数级。本节逐场景给出性质要求、事故案例与可运行代码,收束前两节的全部理论。
**场景一:文件与代码的完整性校验。**软件下载页旁总挂着一串 SHA-256 校验值,包管理器拉取源码包前先核对摘要——防的是传输损坏与下载源被篡改。Git 把这个思想推到极致:每次提交都是整棵目录树内容的哈希摘要,提交本身再被哈希成对象名,任何一次历史改动都会让后续所有对象名对不上——历史不可篡改不是 policy,是数学事实。这个场景对算法的要求最朴素:现役抗碰撞性即可,SHA-256 是默认答案。
场景二:口令存储——唯一"反着用"哈希的场景。数据库泄露已是常态化假设:2012 年 LinkedIn 泄露 1.6 亿条未加盐的 SHA-1 口令哈希,破解者按摘要频率排序,多数口令数日内还原;同年以后的 RockYou 类口令库让攻击者手握数十亿真实口令的字典。这里的对手不是碰撞,而是离线穷举:GPU 对 SHA-256 每秒可算上百亿次,"哈希一遍就存"等于把口令表直接送给枚举器。正确的工程答案由三件事组成:
import hashlib, os, hmac def hash_password(pwd: str) -> bytes: salt = os.urandom(16) # 每用户独立随机盐 dk = hashlib.scrypt(pwd.encode(), salt=salt, # 内存困难型慢哈希 n=2**14, r=8, p=1, dklen=32) return salt + dk # 盐与哈希一同入库 def verify_password(pwd: str, stored: bytes) -> bool: salt, dk = stored[:16], stored[16:] check = hashlib.scrypt(pwd.encode(), salt=salt, n=2**14, r=8, p=1, dklen=32) return hmac.compare_digest(check, dk) # 恒定时间比较,防时序探测 record = hash_password("correct horse battery staple") print(verify_password("correct horse battery staple", record)) # True print(verify_password("wrong guess", record)) # False

场景三:默克尔树——把完整性验证做成对数题。把数据块两两取哈希,再对摘要两两取哈希,逐层向上汇成唯一的树根。此后要证明"某一块在树里且未被改",只需出示该块到树根路径上的兄弟哈希链——一百万笔交易的区块链,验证一笔只需约 20 个摘要。比特币的区块头只存 32 字节的默克尔根,轻节点不下载全部交易即可验证支付;Git 的对象库、BT 下载的分块校验、证书透明度日志,用的都是同一棵树。
| 场景 | 防御对象 | 关键性质 | 推荐选择 |
|---|---|---|---|
| 文件/代码校验 | 篡改与损坏 | 抗碰撞 | SHA-256、BLAKE2 |
| 哈希再签名(4.5 节) | 消息偷换 | 第二原像 | SHA-256 起步 |
| 口令存储 | 离线穷举 | 慢、加盐、内存难 | Argon2、scrypt、bcrypt |
| 默克尔树/数据结构 | 局部篡改 | 雪崩与确定性 | SHA-256(或国密 SM3) |
💡 关键直觉:三个场景其实是同一道题的三种问法——"指纹被伪造的成本是多少"。校验要求伪造成本高于收益,口令要求伪造成本高于全人类算力,默克尔树要求篡改一处必须重算整条链。选型就是给成本定价。
其一,长度与组成:现行标准鼓励长口令而放宽复杂度拼接——强制的特殊字符与定期强制改密已被实践证实会催生规律性弱口令;允许用户记长句,比逼他们记乱码更安全。
其二,失效口令筛查:注册与改密时把候选口令(先加盐哈希)与公开泄露库的摘要比对,命中即拒绝——泄露口令的复用是账户接管的最大单一来源。
其三,胡椒与限速:在慢哈希之外加一道应用级防线——全局密钥(胡椒)在哈希前混入并保存在独立于数据库的系统里,数据库单独泄露时离线穷举立即失效;再配合登录限速与多因素认证,在线穷举的路也被堵死。三则叠加,本节的存储方案才算完整。
登录服务器的常用起点是内存 64 兆字节以上、单次计算约半秒,并以"用户登录体验可接受的最慢值"为准上调;内存参数比时间参数更伤 GPU,优先加大。升级路径靠版本标记:数据库里的每条口令记录都带算法与参数的版本号,用户下次成功登录时用新参数重算并覆盖——口令哈希因此可以平滑换代,而不需要所有人同时改密码。这套"版本化迁移"的思路同样适用于任何密码配置的演进,值得直接搬走。
指纹的三种用法全部落地。下一章进入整机装配:TLS 把签名、证书、ECDHE、AEAD、哈希装进一次握手,本书前面七章的知识点将在浏览器打开网页的一瞬间全部上场。