
你登录网站时输入的密码、从官网下载的软件安装包、Git 中的一次提交,以及 Bitcoin 的区块链,看起来属于完全不同的系统,却都离不开哈希。它们使用哈希的目的也不相同:有的要校验内容,有的要提高猜密码的成本,有的要给对象命名,有的则把历史记录连接起来。
一句话结论:哈希算法把实际工程中的长消息映射为固定长度摘要;安全用途依赖的是特定攻击在现实计算条件下足够困难,而不是“摘要能证明一切”或“数学上绝对不可逆”。
哈希到底做了什么
可以把哈希函数写成:
digest = H(message)message 可以是文本、图片、压缩包或经过规范序列化的数据。哈希函数读取消息,输出固定长度的比特串。以 SHA-256 为例,无论输入是一个字符还是一个大型文件,输出都是 256 bit,也就是 32 byte;通常写成 64 个十六进制字符,因为每个十六进制字符表示 4 bit。
“任意长度输入”是便于理解的科普说法,不应理解为算法规范允许真正无限的输入。具体算法对消息长度存在边界。例如 SHA-256 的消息填充和长度编码受 FIPS 180-4 约束;对普通开发者来说,更准确的表达是:它能处理实际工程中的长消息,并始终给出固定长度结果。

图 2:不同大小的输入经过 SHA-256 后,都得到 256 bit(32 byte、通常显示为 64 个十六进制字符)的摘要。
理解哈希,还要抓住三个直观性质:
- 确定性:同一个算法、同一份字节输入,总会得到同一摘要。注意“看起来相同”的文本可能因编码、换行符或末尾空格不同而产生不同结果。
- 固定输出:输出长度由算法决定,不随输入增大。摘要不是原文的压缩包,不能靠“解压”恢复消息。
- 雪崩效应:好的密码学哈希函数通常追求这样的性质:输入发生很小变化时,输出会广泛变化,而且难以从变化规律推测原输入。这使摘要适合敏感地发现内容差异。
“不可逆”不是准确的安全定义
日常表达常说哈希“不可逆”,但这容易造成两个误解:一是把它当成严格的数学证明,二是以为攻击者无法猜输入。密码学更关心下面三种“在现实资源下难以完成”的能力:
- 原像抗性:给定摘要
h,寻找一个满足H(m) = h的消息m,计算上应当困难。 - 第二原像抗性:给定某个消息
m1,寻找另一个不同消息m2,使二者摘要相同,计算上应当困难。 - 碰撞抗性:寻找任意一对不同消息,使它们摘要相同,计算上应当困难。

图 3:三种安全性质对应不同的攻击目标,不能用一句“不可逆”混为一谈。
固定长度输出意味着输出空间有限,而输入集合远大于输出空间,所以从数学上看,碰撞必然存在。密码学算法承诺的不是“没有碰撞”,而是攻击者在现实计算条件下难以构造有用碰撞。算法是否仍适合安全用途,也要看公开研究、标准与具体应用,不能只看摘要长度。
同样,原像抗性并不会保护低熵输入。若输入只是常见的六位数字,攻击者完全可以枚举候选、逐一计算摘要并比对。哈希函数没有被“逆运算”,但原输入仍可能被猜中。这正是密码不能直接使用快速通用哈希保存的原因。
文件校验:摘要相同能证明什么
下载大型软件后,官网常会公布 SHA-256 摘要。正确的校验链路包含三个步骤:
- 从可信渠道取得官方公布的摘要,例如使用 HTTPS 访问项目官网、已验证的发布页,或检查带数字签名的校验文件。
- 在本地对实际下载的文件重新计算 SHA-256。
- 比较本地结果与可信摘要归一化后的摘要值,可以忽略十六进制
A-F的大小写及展示空白,但不能漏掉任何字符,也不能混淆文件版本。
Get-FileHash .\installer.exe -Algorithm SHA256
图 4:先从可信来源取得摘要,再在本地重算并归一化后比较摘要值;三个环节缺一不可。
结果一致,只能说明“这份文件的内容与那个摘要相匹配”。如果安装包和网页上的摘要同时被攻击者替换,两者仍然可以一致。因此,哈希校验主要解决内容完整性比对;来源真实性还需要 HTTPS、代码签名、发布签名或其他可信分发机制。不能把“摘要一致”扩大解释成“发布者身份必然可信”。
密码存储:为什么不能直接使用 SHA-256
数据库不应保存明文密码,也不应保存一次普通 SHA-256,甚至“SHA-256 加盐”仍然不是现代密码存储的充分方案。SHA-256 为快速处理消息而设计;攻击者拿到数据库后,可以离线高速尝试候选密码,服务器的登录限流此时不起作用。
专用密码哈希或密码派生函数(KDF)通过两类机制提高离线猜测成本:
- 每条记录使用
salt,使相同密码通常产生不同结果,并阻止攻击者直接复用预计算表。 - 使用可调的时间、内存或迭代成本,让每次猜测更昂贵,并能随硬件能力调整参数。
常见选择包括 Argon2id、scrypt、bcrypt 和 PBKDF2。新系统应优先调用成熟框架提供的密码哈希 API,让框架负责安全随机盐、结果编码、参数保存、验证以及将来的重新哈希;不要自行拼接字符串设计格式。

图 5:每条密码记录保存 salt、哈希结果与成本参数;验证时用同一参数重新计算并比较。
NIST SP 800-63B-4 的密码验证要求明确规定:salt **必须(SHALL)至少 32 bit,并且每条记录的 salt 与哈希结果都必须保存;成本因子应当(SHOULD)**在不对验证性能产生负面影响的前提下尽可能高,并随硬件性能调整。这不是要求永远采用某个固定迭代次数,而是要求系统能维护和升级参数。
对 Argon2,RFC 9106推荐密码哈希使用 16 byte salt;文档在空间受限场景讨论过 64 bit,但不应把它当作所有实现的统一强制下限。为每条密码生成独立、不可预测的 salt 是稳妥的工程实践,但不要把“盐必须全局唯一”错误表述成 RFC 9106 的原文强制条款。salt 可以随记录公开保存;它不是需要保密的加密密钥。
同叫“哈希”,工程目标却不同
哈希表:重点是快速查找
哈希表利用哈希值把键分布到桶中,以便快速定位数据。它通常追求计算快、分布均匀和较低冲突率,并不要求原像抗性或碰撞抗性。若键来自不可信输入,还要考虑攻击者制造大量冲突导致性能退化,但解决方案也不等于一律换成 SHA-256。数据结构哈希与密码学哈希应按威胁模型选择。
Git:对象 ID 来自规范表示
传统 Git 并非只对文件内容裸算 SHA-1。Git 对对象的规范序列化表示计算摘要,其输入形式可概括为:
<type> <length>\0<content>所得 SHA-1 用作对象 ID,因此对象类型、长度和内容共同影响标识。Git 的哈希函数迁移设计选择 SHA-256 作为新哈希方案,并描述了不同格式之间的兼容与映射问题;这不表示所有现有仓库已经默认使用 SHA-256。讨论某个仓库时,应先确认它实际采用的对象格式。
Bitcoin:哈希负责连接,安全来自完整机制
Bitcoin 区块头包含前一区块的哈希。若历史区块内容改变,其区块哈希会变化,后续区块中记录的连接也随之失效,攻击者必须从被修改区块起,重做该区块自身及后续区块的工作量证明(PoW)。可是,“使用哈希”本身并不会自动产生不可篡改性。
Bitcoin 的历史修改成本还依赖工作量证明(Proof of Work,PoW)、链选择规则、网络传播以及多数诚实算力等假设。Bitcoin 白皮书把这些机制放在一个整体中讨论。准确说法是:哈希提供内容承诺与链式连接,PoW 和共识假设让重写历史在特定威胁模型下变得昂贵;不能简化成“有哈希就不可篡改”。
场景选择:先问目标,再选工具
| 场景 | 核心目标 | 推荐做法 | 常见误区 |
|---|---|---|---|
| 文件下载校验 | 检查内容是否匹配 | 从可信来源取得摘要,本地重算并归一化后比较摘要值 | 把摘要一致当成发布者身份认证 |
| 密码存储 | 提高离线猜测成本 | 使用成熟框架的 Argon2id、scrypt、bcrypt 或 PBKDF2 API | 直接用 SHA-256,或认为 SHA-256 加盐就足够 |
| 哈希表 | 快速分布与查找 | 使用语言或框架的数据结构实现,结合输入威胁评估 | 默认要求密码学安全 |
| Git 对象 | 内容寻址与完整性标识 | 依据仓库对象格式使用 Git 自身机制 | 认为所有仓库默认都是 SHA-256 |
| 区块链 | 内容连接与共识记账 | 同时分析哈希、PoW、链规则与参与者假设 | 认为仅靠哈希即可防篡改 |
可以复述的总结
哈希算法把工程中的长消息映射为固定长度摘要,具有确定性;好的密码学哈希函数通常追求雪崩效应。SHA-256 输出 256 bit,也就是 32 byte,通常显示为 64 个十六进制字符。安全性应分别讨论原像抗性、第二原像抗性与碰撞抗性;理论碰撞必然存在,关键是现实攻击成本。
使用时记住三句话:文件校验必须先有可信摘要来源;密码必须交给带盐和可调成本的专用密码哈希;Git 与区块链中的哈希只是系统机制的一部分。先明确目标与威胁模型,再选择算法和 API,才是哈希真正可靠的用法。
评论(0)