Skip to content

哈希(Hash)原理与常见应用

难度等级:🟡 中级(2-4年)
分类:工程化
归档日期:2026-09-03


📚 理论知识

一、什么是哈希

哈希是一种单向函数,把任意长度的输入(文字、文件等)转换成一段固定长度的"指纹"(哈希值)。

核心特性:

  • 单向性:正向计算容易,反向推导原始数据几乎不可能(这是哈希和加密最本质的区别)
  • 确定性:相同输入永远得到相同输出
  • 雪崩效应:输入哪怕只改一个字符,输出也会完全不同
  • 固定长度:无论输入多长,输出长度始终一致(如 SHA-256 恒为 256 位)

二、常见误解:密码"用哈希加密传输"

这个说法混淆了两件事,实际上分工是:

环节使用技术目的
密码从客户端传到服务器加密(HTTPS/TLS,可逆)防止中间人窃听
密码在服务器中存储哈希(如 bcrypt,不可逆)防止数据库泄露后密码被直接看到

加密 vs 哈希的本质区别:

加密哈希
是否可逆可逆(有密钥可解密还原原文)不可逆,没有"解哈希"这回事
输出长度通常与原文相关固定长度
目的保密,需要还原原文生成指纹,用于比对、校验
例子AES、RSA、TLSSHA-256、MD5、bcrypt

正确表达应为「密码传输用加密,存储用哈希」,而不是「用哈希加密」——因为哈希本身设计上就不打算被还原,这是它的核心目的而非局限。


三、"固定 256 位"是什么意思

  • 不管输入多长,SHA-256 的输出永远是 256 位(bit)
  • 256 位 = 32 字节 = 十六进制表示下的 64 个字符(每个十六进制字符代表 4 位)
  • 例:8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c(正好 64 位字符)

由于任意长度的输入都要压缩进固定长度的输出,理论上存在哈希碰撞的可能(不同输入产生相同输出),但 2^256 的组合数极其庞大,实际概率可忽略不计。


四、文件是怎么哈希的

对哈希算法来说,文件本质上和字符串一样,都是一长串二进制字节,只是通常长得多。

计算过程:

  1. 读取文件的原始字节内容(不是"看到的文字",而是磁盘上的二进制数据)
  2. 哈希算法把数据切成固定大小的块(SHA-256 每块 512 位),逐块处理,并将结果不断"揉入"后续计算
  3. 最终输出固定长度(256 位)的哈希值,无论文件是 1KB 还是 10GB

典型用途:

  • 文件完整性校验:下载文件后核对哈希值,判断是否被篡改或损坏
  • 文件去重:云盘通过哈希判断文件是否已存在("秒传"的原理之一)
  • 版本控制:Git 用哈希标识每次提交的内容

五、哈希计算耗时与文件大小的关系

计算时间与文件大小基本呈线性关系:文件越大,需要处理的"块"越多,耗时越长。

大致耗时参考(普通电脑,SHA-256):

文件大小大致耗时
几十字节文本微秒级
1MB 文档毫秒级
1GB 视频约 1~几秒
10GB 安装包十几秒到几十秒

影响耗时的因素:

  1. 算法复杂度:MD5/SHA-1 较轻量但不安全;SHA-256/SHA-3 更安全但稍慢;bcrypt/Argon2 是故意设计得很慢(用于拖慢密码暴力破解)
  2. 硬件:现代 CPU 的专用指令集可大幅加速计算
  3. 实际瓶颈往往是文件读取的 I/O 速度,而非哈希计算本身

六、常见哈希算法分类

通用哈希算法(快,用于完整性校验):

算法输出长度安全性
MD5128位❌ 已被攻破,不应用于安全场景
SHA-1160位❌ 已被证明存在碰撞漏洞
SHA-256256位✅ 主流安全选择(比特币、数字签名等)
SHA-3可变✅ 新一代标准,安全性更高

密码专用哈希算法(故意设计得慢):

  • bcryptscryptArgon2
  • 每次计算耗时几十到几百毫秒,目的是大幅拉高黑客批量破解密码的成本
  • 存储密码的推荐做法:使用 bcrypt 或 Argon2,而非 SHA-256(SHA-256 太快,抗暴力破解能力较弱)

七、扩展:计算机存储单位换算

基础单位:

1 字节 (Byte, B) = 8 位 (bit, b)

⚠️ 注意大小写:小写 b = 位,大写 B = 字节。网速常用 Mbps(兆位/秒),下载速度显示为 MB/s(兆字节/秒):

100 Mbps(网速) ÷ 8 = 12.5 MB/s(实际下载速度)

二进制 vs 十进制换算差异:

单位严格二进制(1024进位)日常十进制(1000进位,硬盘厂商常用)
KB1024 B1000 B
MB1024 KB1000 KB
GB1024 MB1000 MB
TB1024 GB1000 GB

这也是为什么标称 1TB 的硬盘插入电脑后显示约 931GB——并非商家缺斤少两,而是十进制标称值除以二进制单位换算后的结果:

1,000,000,000,000 字节 ÷ 1024³ ≈ 931 GB

完整单位链条:

1 字节 (B)  = 8 位 (bit)
1 KB       ≈ 1024 B(或 1000 B)
1 MB       ≈ 1024 KB
1 GB       ≈ 1024 MB
1 TB       ≈ 1024 GB
1 PB       ≈ 1024 TB

常见误区

问题要点
把哈希叫成"加密"哈希不可逆,没有"解哈希"这回事;加密是可逆的,两者目的完全不同
用 SHA-256 直接存密码SHA-256 计算太快,攻击者用彩虹表/GPU 暴力破解成本很低,密码存储要用故意设计得慢的 bcrypt/Argon2
认为哈希"绝对不会碰撞"理论上一定存在碰撞(鸽笼原理),只是 256 位空间下概率小到可忽略,MD5/SHA-1 已被证明能人为构造出碰撞
以为哈希输出长度和输入长度有关系无论输入是 1 个字符还是 10GB 文件,同一算法输出长度恒定

❌ 错误答法示范

错误示例 1:「密码在数据库里是用哈希加密存的,需要的时候后端会把它解密出来做比对。」
❌ 问题:哈希不可逆,根本没有"解密"这一步。正确做法是把用户输入的密码用同样的算法(加盐)再哈希一次,拿两个哈希值做比对,而不是还原出明文密码。

错误示例 2:「哈希算法不会有两个不同的输入算出相同的输出,因为哈希值是唯一的。」
❌ 问题:输入空间是无限的,输出空间是有限的(256 位),根据鸽笼原理,碰撞在数学上必然存在,只是 SHA-256 这种安全算法找到一次碰撞的计算代价极其高昂,实际不可行;而 MD5、SHA-1 已经被证明可以在合理时间内构造出碰撞,所以才被判定为不安全。


🎤 模拟面试作答

嗯,这道题我从哈希是什么、和加密的区别、以及实际应用场景这几块来说。

哈希本质上是一个单向函数,把任意长度的输入压缩成固定长度的一段"指纹"。它有几个关键特性:单向不可逆,输入几乎不可能从哈希值反推出来;确定性,相同输入永远得到相同输出;还有雪崩效应,输入哪怕改一个字符,输出也会完全不一样,这个特性让它非常适合做完整性校验。

很多人容易把哈希和加密混为一谈,但这是两回事:加密是可逆的,有密钥就能解密还原出原文,用于保密传输,比如 HTTPS 用的 TLS;哈希是不可逆的,没有"解哈希"这个操作,目的是生成一个指纹用来比对,而不是还原数据。所以密码这块正确的说法应该是"传输用加密、存储用哈希",不能说"用哈希加密",因为哈希设计的初衷就是不能被还原,这是它的核心目的,不是局限性。

具体到密码存储,我们不会直接用 SHA-256 去存,因为它计算太快了,攻击者用 GPU 批量跑彩虹表或者暴力破解成本很低。业界推荐用 bcrypt 或者 Argon2 这类专门为密码设计的哈希算法,它们故意把单次计算做得很慢,比如几十到几百毫秒,正常登录场景用户感知不到,但会大幅拉高攻击者批量破解的成本。

哈希在其他场景也很常见,比如文件完整性校验——下载完文件核对哈希值判断有没有被篡改;云盘的秒传功能,本质上是拿文件哈希去查库里有没有相同的文件,有的话直接建立引用不用重新上传;Git 也是用哈希来标识每一次提交的内容。

另外哈希碰撞这个点也值得提一下:因为输入空间是无限的,输出空间是固定的 256 位,理论上碰撞是必然存在的,只是 SHA-256 这种安全算法要找到一次碰撞的计算量大到不可行;但像 MD5、SHA-1 已经被证明能在合理时间内人为构造出碰撞,所以现在都不建议用在安全相关的场景了。

……如果面试官感兴趣,我还可以聊聊加盐(salt)是怎么进一步防止彩虹表攻击的,或者展开讲讲 Git 具体是怎么用哈希组织提交历史的。


❓ 面试官追问预测

追问 1:光哈希密码就够安全了吗?如果两个用户密码一样,哈希值会不会一样,这样有什么问题?

应对思路:会一样,这正是纯哈希的隐患——攻击者可以预先生成一张"常见密码 → 哈希值"的彩虹表,拿数据库里泄露的哈希值直接反查。解决办法是加盐(salt):给每个用户的密码额外拼接一段随机字符串再哈希,盐值随哈希结果一起存起来,这样即使两个用户密码相同,加盐后的哈希值也不同,彩虹表直接失效。bcrypt 本身就内置了加盐机制。

追问 2:既然哈希碰撞理论上一定存在,那 Git 用哈希标识提交内容,会不会因为碰撞导致两个不同的提交被误判成同一个?

应对思路:理论上可能,但 Git 用的 SHA-1(新版本正迁移到 SHA-256)在 2^160 的空间下,随机碰撞的概率低到实际工程中可以忽略;真正的风险是"刻意构造"碰撞(比如 2017 年的 SHAttered 攻击针对 SHA-1),这也是社区推动 Git 迁移到 SHA-256 的原因之一。

追问 3:为什么 bcrypt 这种密码哈希要故意设计得慢,直接用 SHA-256 加盐不行吗?

应对思路:SHA-256 加盐确实能防彩虹表,但它计算速度极快(现代 GPU 每秒能算几十亿次),攻击者拿到哈希库后可以用暴力破解/字典攻击在短时间内跑完常见密码空间;bcrypt/Argon2 通过内置的"工作因子"把单次计算拖慢到几十到几百毫秒,正常登录用户感知不到延迟,但暴力破解的总耗时会被放大几个数量级,这是专门针对密码场景的权衡设计。

追问 4:文件去重/秒传功能,只用文件哈希判断"文件已存在"安全吗?会不会被人利用哈希碰撞伪造一个文件顶替别人的文件?

应对思路:理论上存在风险,如果只用哈希值当唯一标识且允许用户上传任意内容,攻击者构造出碰撞文件就可能覆盖或冒充别人的文件。工程上通常会用 SHA-256 这种目前没有已知实用碰撞攻击的算法,并且结合文件大小、上传者权限等额外校验,而不是完全只信任一个哈希值。

追问 5:哈希和 CRC32 这类校验和(checksum)有什么区别?

应对思路:CRC32 设计目的是快速检测随机比特错误(比如网络传输、存储介质的意外损坏),计算极快但不具备抗碰撞、抗篡改的安全属性,几行代码就能构造出 CRC32 相同的不同数据;哈希(尤其是 SHA-256 这类密码学哈希)设计目标包含抗碰撞、抗篡改,计算成本更高,用于安全校验、数字签名等场景。两者都能做"完整性校验",但抗恶意篡改的能力完全不在一个量级。

个人面试题归档,持续更新中