SHA-1 为什么被攻破?一文讲清哈希碰撞与 SHAttered

SHA-1 为什么不安全?小编用大白话讲清哈希函数、哈希碰撞与 2017 年 SHAttered 攻击,并告诉您 Git、文件校验和密码存储该怎么选哈希算法。

您是不是在下载页、Git 提交记录或者安全文章里见过一长串 40 位的十六进制字符,却不太确定它到底是什么、安不安全?简单讲:那是 SHA-1 算出来的“指纹”,而 SHA-1 早就不能再当作安全的指纹来用了。

结论先说:2017 年,谷歌和荷兰 CWI 研究所做出了两份内容不同、SHA-1 值却完全一样的 PDF,这次攻击叫 SHAttered。它证明 SHA-1 已经抵挡不住“故意制造碰撞”,新系统请改用 SHA-256 或 SHA-3;存密码则要用专门的慢哈希算法。

下面小编按“哈希是什么、碰撞为什么危险、攻击怎么做到的、现在该怎么办”的顺序,带您捋一遍。

什么是密码学哈希函数?

哈希函数可以把任意大小的输入(一个密码、一份文档、一个安装包)压缩成固定长度的结果,这个结果叫“哈希值”或“消息摘要”。SHA-1 的结果是 160 位,通常写成 40 个十六进制字符。

一个合格的密码学哈希函数,一般要满足四点:

  • 确定性:同样的输入,永远得到同样的输出。
  • 计算快:处理大文件也不费劲。
  • 单向性:拿到哈希值,推不回原文。
  • 抗碰撞:想故意找到两份不同内容、却得到相同哈希值,在现实中应该做不到。

哈希函数把任意输入变成固定长度摘要的示意图

SHA-1 栽的跟头,就在第四条:研究人员证明,故意制造碰撞已经能以可接受的成本做到。

什么是哈希碰撞?

碰撞,就是两份不同的输入算出了同一个哈希值。从数学上讲碰撞一定存在,因为输入是无限的,输出长度却固定。安全设计的要求不是“碰撞不存在”,而是“想故意找到一对,要花的算力大到不现实”。

对一个理想的 160 位哈希,利用“生日悖论”,找一对碰撞大约需要 280 次运算,这已经远超暴力计算的能力。SHA-1 的问题在于,密码分析者找到了数学上的捷径,把所需工作量压到了远低于这个理想值。

SHAttered 攻击到底发生了什么?

2017 年 2 月 23 日,谷歌和荷兰 CWI 研究所宣布,他们找到了完整 SHA-1 的首个实用碰撞,并公开了两份看上去不同、SHA-1 值却相同的 PDF。详情可以看谷歌的官方公告,以及 CWI 的研究发布。

这次计算规模很大,但对资金充足的团队是可行的。谷歌公布的数字大致是:

  • 约 9.22 × 1018 次 SHA-1 运算;
  • 第一阶段相当于 6500 年的 CPU 计算量;
  • 第二阶段相当于 110 年的 GPU 计算量。

注意,这些“年”是把大量机器的算力加总后的折算值,并不是一台电脑真的跑了几千年。谷歌估计,这种攻击比通用暴力碰撞快 10 万倍以上。

两份不同的 PDF,为什么能有相同的哈希?

SHA-1 分块处理与内部状态变化示意图

SHA-1 是按块处理数据的:每处理一块,就用一连串位运算更新一次内部状态。研究人员并不是碰运气,而是用“差分密码分析”,研究两份消息之间精心设计的差异,在内部计算中会怎样传播。

他们构造出特殊的“碰撞块”,让两份消息先分叉,再把 SHA-1 的内部状态重新拉回到一起。内部状态一致后,两份文件接上相同的后续内容,最终摘要自然一样。这次成功的关键,是利用了 SHA-1 结构上的弱点,而不是单靠蛮力去撞整个 160 位空间。

SHA-1 碰撞危险在哪?

很多系统把哈希值当作数据的“身份证”。如果它默认“一个哈希值只对应一份内容”,那么被人故意制造的碰撞就会打破这个假设。受影响的场景包括:

  • 依赖抗碰撞性的数字签名;
  • 文件完整性校验;
  • 证书体系;
  • 内容寻址存储;
  • 版本控制系统里的对象标识;
  • 把哈希当唯一标识的去重系统。

要说清楚一点:碰撞攻击不等于“能反推出密码”。那是另一种安全属性。SHA-1 被证明失守的是抗碰撞性,不过它已经过时到,新设计根本不该再用。

2017 年之前,SHA-1 就已经被怀疑了

2005 年前后,就有重要的密码分析成果表明 SHA-1 的抗碰撞强度明显低于理论上的 80 位。美国 NIST 随后推动大家转向 SHA-2。NIST 自己的说明里提到,2011 年弃用 SHA-1,2013 年底起禁止用于数字签名。所以 2017 年的碰撞,更像是给一个早已开始的迁移再添一把火。

想看原文,可以读 NIST 对这次研究的官方回应。

SHA-1 现在还能用吗?

“抗碰撞场景下已经被攻破”和“任何历史用途都立刻作废”,是两回事。NIST 在过渡期里仍允许少数遗留用法,比如验证部分旧签名,或不依赖抗碰撞性的特定构造。但官方态度很明确:能迁走就尽快迁走。

NIST 计划在 2030 年 12 月 31 日前完成这一过渡,此后 SHA-1 不再是 FIPS 140 验证密码模块中的批准算法。最新政策见 NIST 哈希函数政策页面。

用什么替代 SHA-1?

新系统需要通用密码学哈希时,NIST 推荐 SHA-2 或 SHA-3 家族。SHA-256 属于 SHA-2,是目前最常见的默认选择。截至 2026 年,公开资料里还没有能像 SHAttered 攻击 SHA-1 那样攻击 SHA-256 的实用碰撞方法。

算法在新安全设计中的状态建议
MD5已被攻破不要用于需要抗碰撞的安全场景
SHA-1抗碰撞性已被攻破尽快迁移旧系统
SHA-256(SHA-2)当前批准的算法家族多数场景的好默认值
SHA-3当前批准的算法家族内部设计不同,是有力的备选

为什么迁移 SHA-1 这么慢?

遗留系统中 SHA-1 迁移的复杂依赖示意图

换个哈希算法,听起来只是改一行代码。可一旦哈希写进了长期使用的数据格式或信任体系,事情就复杂了。大型机构里,SHA-1 可能藏在这些地方:

  • 很难升级的固件;
  • 仍需验证的旧数字签名;
  • 证书数据库;
  • 文件归档和去重索引;
  • 工业系统;
  • 老协议;
  • 应用接口与序列化数据格式;
  • 被脚本、数据库和文档引用的版本控制标识。

迁移时要回答一堆兼容性问题:旧标识还有效吗?旧软件能读新格式吗?两套哈希要不要并存一段时间?历史签名怎么继续验证?这就是为什么即使替代算法早已成熟,密码迁移也常常要拖上好几年。

Git 和 SHA-1 是什么关系?

Git 历史上用 SHA-1 给对象命名:每个提交、目录树、文件内容和带注释的标签,都有一个由内容算出的 SHA-1 标识。所以 SHAttered 对软件开发圈格外有冲击——直觉上,两个不同的 Git 对象似乎也可能被造成同一个身份。

Git 后来加入了多重防护,也一直在推进向 SHA-256 的过渡。官方文档描述了一种用 SHA-256 命名对象的仓库格式,并可维护 SHA-256 与 SHA-1 名称之间的对照。详见 Git 的哈希函数过渡文档。不过这不代表所有仓库都已经转换,兼容现有工具、托管服务和老仓库,比换个函数调用难得多。

日常提交代码,会中 SHA-1 攻击吗?

普通开发不必看到每个 40 位提交号就紧张。实用碰撞攻击需要精心构造的数据,现代 Git 和托管平台也部署了针对 SHAttered 的碰撞检测。更大的启示是架构层面的:不要再把纯 SHA-1 的抗碰撞性当作可信的安全边界。

SHA-1 能用来存密码吗?

不能。就算撇开碰撞不谈,纯 SHA-1 也不适合存密码,因为它设计得太快,攻击者每秒能试海量猜测。存密码要用专门的慢哈希或密钥派生函数,并加盐、设置合适的工作量,比如 Argon2id、scrypt、bcrypt 或 PBKDF2。把 SHA-1 换成纯 SHA-256,并不会自动让一个不安全的密码库变安全。场景和算法一样重要。

哈希不等于加密

哈希是单向的;加密则是让持有密钥的人能还原原文。用安全的加密算法加密文件,保留密钥就能解密;而对文件算 SHA-256,没有任何“反哈希”操作能从摘要还原文件。哈希、加密、数字签名和密码哈希,解决的是不同的问题。

怎么用 SHA-256 校验下载文件?

如果官方提供了 SHA-256 值,您可以对自己下载的文件算一遍再对比。

Windows PowerShell:

Get-FileHash "C:\path\to\file.iso" -Algorithm SHA256

Linux:

sha256sum filename.iso

macOS:

shasum -a 256 filename.iso

两个值必须逐字符一致。不一致,说明您的文件和发布者对应的文件不是逐字节相同。还要提醒一句:校验值本身必须来自可信渠道。如果攻击者能同时替换下载文件和页面上的校验值,对比就证明不了真伪。

常见问题(FAQ)

SHA-1 是什么时候被攻破的?

弱点很早就有人指出,但完整 SHA-1 的首个实用碰撞,是 2017 年 2 月 23 日由谷歌和 CWI 公开的。

真的有两个不同文件的 SHA-1 值一样吗?

有。SHAttered 项目做出了两份看起来不同、SHA-1 摘要完全相同的 PDF。

SHA-1 碰撞会泄露密码吗?

不会。碰撞攻击和破解密码是两回事。不过纯 SHA-1 依然不适合存密码,因为太快,也缺少现代密码哈希的保护。

SHA-256 被攻破了吗?

截至 2026 年,公开资料里没有与 SHAttered 同级别的 SHA-256 实用碰撞攻击,NIST 仍批准 SHA-2 家族。

新软件还要不要用 SHA-1?

不要。需要抗碰撞的通用哈希时,选 SHA-256 或其他获批的 SHA-2/SHA-3 即可。

为什么 Git 里还是 40 位的哈希?

Git 历史悠久、兼容要求高,迁移很复杂。Git 已支持向 SHA-256 仓库过渡,但现有的 SHA-1 仓库和标识依然非常普遍。

小结

SHA-1 不是因为电脑碰巧算出了两个相同的值而失败,而是研究人员找到了结构性攻击,让“故意制造碰撞”变得可行。SHAttered 是决定性的演示,但警告信号早在十多年前就出现了。这给密码学上了一课:算法不会因为曾经被认为安全,就永远可信。

小编的建议很简单:新系统别再把 SHA-1 当抗碰撞的安全组件;普通哈希用 SHA-2 或 SHA-3;存密码用专门的密码哈希;设计系统时,也留好“将来能换算法”的余地。