sha256sum 校验文件完整性,用 hashcat 破解 MD5 哈希,区分编码与加密的本质差异,理解 OWASP 2026 推荐的 Argon2id 密码哈希方案,并掌握 HMAC 消息认证的原理。上一篇你学的对称加密和非对称加密都是"可逆"的——给密钥就能还原明文。哈希算法(hash function)截然不同:它是单向的,一旦把数据送进去算出哈希值,就再也回不来了。这听起来很反直觉,但正是这种"不可逆"让哈希成为密码学中不可替代的工具。
打个比方:加密像把信件锁进保险箱,有钥匙就能打开;哈希像把一块牛肉做成牛肉干——你可以验证"这块牛肉干确实来自那块牛肉"(通过对比纹理),但谁也无法把牛肉干还原回生鲜牛肉。哈希算法就是把输入数据"压缩"成一串固定长度的指纹,这个指纹可以用来验证数据完整性,但无法从指纹反推原始数据。
一个合格的密码学哈希函数必须满足五个特性:
光说概念太抽象,直接动手算。在 Kali 终端里用 sha256sum 命令计算字符串 "hello" 的 SHA-256 哈希值:
"hello" 和 "Hello" 只差一个字母的大小写,但两个哈希值完全不同——这就是雪崩效应。你无法从这两个哈希值中看出任何关联,也无法反推出原始输入是 "hello" 还是 "Hello"。
echo "hello" 会在末尾附加换行符 \n,算出来的哈希和 printf "hello" 不同。验证文件哈希时用 sha256sum 文件名 即可,文件内容不含额外换行问题。你已经知道了哈希的五大特性,其中"抗碰撞"是最关键的安全指标。一旦某个哈希算法被证明可以高效构造碰撞,它就不再安全。哈希算法的发展史,就是一部碰撞攻击与算法升级的军备竞赛。
| 算法 | 输出长度 | 安全状态 | 关键事件 |
|---|---|---|---|
| MD5 | 128 位 | 已淘汰 | 2004 年王小云团队发表碰撞攻击 |
| SHA-1 | 160 位 | 已淘汰 | 2017 年 Google SHAttered 公开碰撞 |
| SHA-256 | 256 位 | 安全 | SHA-2 家族,当前主流 |
| SHA-3 | 224-512 位 | 安全 | Keccak 结构,与 SHA-2 无关 |
2004 年,中国密码学家王小云团队发表了针对 MD5 的高效碰撞攻击方法——能在几分钟内找到两个内容不同但 MD5 值相同的文件。这意味着攻击者可以伪造一份"哈希值不变但内容被篡改"的文件,彻底击碎了 MD5 的完整性校验能力。2017 年 Google 实施了 SHAttered 攻击,用相同的 SHA-1 值生成了两份内容不同的 PDF 文件,宣告 SHA-1 也步入历史。
虽然 MD5 和 SHA-1 已不安全,但它们仍然大量存在于遗留系统中。渗透测试时经常遇到用 MD5 存储密码的网站——这对攻击者来说是好消息,因为 GPU 破解 MD5 的速度极快。下面用 Kali 自带工具实际对比各算法的输出差异:
可以看到 MD5 输出 32 个十六进制字符(128 位),SHA-1 输出 40 个字符(160 位),SHA-256 输出 64 个字符(256 位)。输出越长,碰撞空间越大,抗碰撞能力越强。
上一章你看到了 MD5 为什么不能存密码——它太快了。普通哈希算法(MD5/SHA-256)的设计目标是"快速计算",这在文件校验场景下是优点,但在密码存储场景下变成了致命缺陷:攻击者拿到哈希值后,可以用 GPU 每秒尝试数百亿次密码。
密码哈希算法(password hashing)的设计目标恰恰相反——它要慢。而且不是固定地慢,是可以随硬件升级而"调慢"的。这样即使 GPU 算力翻倍,你只需调高参数就能维持同样的破解难度。
bcrypt 是 1999 年设计的密码哈希算法,核心机制是 cost factor(成本因子)。cost 每增加 1,计算时间翻倍。cost=12 意味着 2^12 = 4096 轮迭代,单次哈希约 250ms——对用户登录几乎无感,但对暴力破解是噩梦。bcrypt 的哈希值自带算法标识、cost、盐和哈希结果:
这个哈希字符串的每一段都有含义:$2b$ 是算法版本标识,12 是 cost 因子,接下来 22 个字符是盐(salt),最后 31 个字符是哈希结果。验证密码时只需把用户输入和这整个字符串传给 bcrypt.checkpw(),bcrypt 会自动提取 salt 和 cost 重新计算并比对。
bcrypt 的弱点是不消耗内存——GPU 有海量计算核心但内存有限,bcrypt 恰好让 GPU 可以并行跑满。Argon2 是 2015 年密码哈希竞赛的冠军算法,它的核心创新是内存硬(memory-hard):哈希计算时必须占用大量内存(默认 19MiB+),让 GPU 和 ASIC 的并行优势大幅缩水。OWASP 2026 密码存储指南推荐 Argon2id 为首选方案:
| 算法 | OWASP 地位 | 推荐参数 | 内存硬 |
|---|---|---|---|
| Argon2id | 首选 | m=19MiB, t=2, p=1 | 是 |
| bcrypt | 推荐 | cost≥12 | 否 |
| PBKDF2 | FIPS 备选 | iterations≥600000 | 否 |
Linux 系统的 /etc/shadow 文件存储用户密码哈希。你可以查看自己 Kali 系统的 shadow 文件格式(注意需要 root 权限):
Kali Linux 默认使用 yescrypt($y$)作为密码哈希算法,这也是一种内存硬哈希。如果在 /etc/shadow 中看到 $6$ 则是 SHA-512 crypt,看到 $1$ 就是古老的 MD5 crypt——后者意味着系统需要升级密码策略了。
上一篇的小贴士里提到过"编码不是加密"。这个区别在安全领域至关重要——很多初学者看到 Base64 字符串就以为是"加密了",实际上任何人都能秒解。理解编码的本质,是避免安全误判的前提。
核心区别只有一句话:加密需要密钥才能还原,编码不需要任何密钥。编码只是换了一种数据表示方式,目的是"让数据能在特定环境中传输或存储",而不是"隐藏数据内容"。就像把中文翻译成英文——懂英文的人都能读,不存在"密钥"概念。
| 特性 | 加密 | 哈希 | 编码 |
|---|---|---|---|
| 可逆性 | 可逆(需密钥) | 不可逆 | 可逆(无需密钥) |
| 安全性 | 提供机密性 | 提供完整性 | 无安全性 |
| 典型代表 | AES、RSA | SHA-256、bcrypt | Base64、Hex、URL |
在 Kali 中用命令行实际操作三种最常见的编码:
上面的 Base64 例子正是 HTTP Basic Authentication 的实际格式——浏览器发送 Authorization: Basic YWRtaW46cGFzc3dvcmQxMjM= 时,服务端只需 base64 -d 就能还原出明文密码。这就是为什么 Basic Auth 必须配合 HTTPS 使用——否则凭据等于明文传输。
= 或 == 结尾、字符集为 A-Z/a-z/0-9/+//,长度是 4 的倍数。Kali 自带的 CyberChef 工具可自动识别多种编码格式。理解了哈希算法和编码的区别,现在进入实战环节。哈希在安全领域有两个高频应用场景:一是验证文件完整性(确认下载的文件没被篡改),二是密码破解(从哈希值反推密码明文)。前者是防御手段,后者是攻击手段——渗透测试中两者都会用到。
下载 Kali ISO 镜像时,官方网站会提供 SHA-256 校验值。下载完成后用 sha256sum 对比,确保文件在传输过程中没有被篡改或损坏:
hashcat 是 Kali 自带的高性能密码破解工具,支持 CPU/GPU 加速。它用 -m 指定哈希类型,-a 指定攻击模式。常见的哈希类型编号:
| -m 编号 | 哈希类型 | GPU 破解速度(参考) |
|---|---|---|
| 0 | MD5 | ~100 GH/s |
| 100 | SHA-1 | ~70 GH/s |
| 1400 | SHA-256 | ~30 GH/s |
| 3200 | bcrypt (cost=5) | ~50 KH/s |
上表用 RTX 4090 的参考速度展示了为什么 MD5 不能存密码——GPU 每秒可算 1000 亿次 MD5,8 位纯数字密码(1 亿种组合)不到 1 毫秒就穷举完了。而 bcrypt 即使在 cost=5(远低于推荐的 12)时,速度也骤降至每秒 5 万次——慢了 200 万倍。这就是密码哈希算法存在的意义。
下面用 hashcat 执行一次字典攻击。Kali 自带 /usr/share/wordlists/rockyou.txt 字典文件(需先解压):
哈希值 5f4dcc3b5aa765d61d8327deb882cf99 正是 "password" 的 MD5 值。hashcat 在 rockyou 字典(约 1400 万条密码)中瞬间找到了它。这就是为什么密码不能是常见词——字典攻击对弱密码几乎是秒破。
--force 会跳过 GPU 检测警告,在无 GPU 的树莓派上可能导致结果不可靠
john --format=raw-md5 --wordlist=rockyou.txt target.hash 替代,CPU 模式更稳定
前五章你学会了计算哈希、区分编码、破解弱密码哈希。但还剩两个问题没解决:第一,攻击者可以预计算一张"密码→哈希"对照表(彩虹表),拿到哈希值后直接查表——比逐个暴力破解快几个数量级。第二,普通哈希只能验证"数据没变",但无法验证"数据是谁发的"——任何人都能算 SHA-256,攻击者可以篡改数据后重新计算哈希替换掉。
彩虹表的原理是"空间换时间"——预先计算好几亿个常见密码的哈希值存成巨型对照表。对抗方法很简单:在密码后面拼接一段随机字符串(称为盐,salt)再哈希。攻击者的彩虹表只针对无盐哈希,一旦加了随机盐,预计算的表就全废了——必须为每个盐值重新算一张表,得不偿失。
bcrypt 和 Argon2id 自带盐值处理——你不需要手动管理盐。但如果用 PBKDF2 或 SHA-256,必须自己生成随机盐并和哈希值一起存储。盐值不需要保密,只需要保证每个用户不同且足够随机(至少 16 字节)。
HMAC(Hash-based Message Authentication Code)解决了"谁发的"问题。它在哈希计算中混入一个密钥——只有持有密钥的人才能生成正确的 HMAC。接收方用同样的密钥重新计算 HMAC,如果匹配则证明消息确实来自持有密钥的人且未被篡改。
HMAC 最常见的应用是 JWT(JSON Web Token)签名和 API 请求签名。下面用 Python 演示 HMAC 的生成与验证:
hmac.compare_digest() 而非 ==。普通 == 在第一个不匹配字符处就返回 False,攻击者可通过测量响应时间逐字节猜出正确签名(时序攻击)。compare_digest 始终比较完整字符串,耗时恒定。回顾本篇三个核心知识点:哈希算法提供完整性验证(数据没被篡改),盐值提供彩虹表防御(每个用户哈希唯一),HMAC 提供消息认证(确认发送方身份)。这三者和上一篇学的加密算法组合在一起,就构成了数字证书和 TLS 握手的基础——下一篇将把这些零件拼装成完整的 PKI 信任体系。
test.txt,写入任意内容,用 sha256sum test.txt > hash.txt 保存哈希。然后修改文件内容(加一个字),再执行 sha256sum -c hash.txt,观察输出变化。sha256sum -c hash.txt 会输出 test.txt: FAILED,并提示 sha256sum: WARNING: 1 computed checksum did NOT match。即使只改了一个字,哈希值也完全不同。YWRtaW46MTIzNDU2 是一段 Base64 编码。请用 Kali 命令行解码它,然后对解码后的字符串计算 MD5 哈希,最后用 hashcat 和 rockyou 字典尝试破解这个 MD5。echo "YWRtaW46MTIzNDU2" | base64 -d 解码得到 admin:123456。对 "123456" 计算 MD5 得到 e10adc3949ba59abbe56e057f20f883e,写入文件后 hashcat -m 0 -a 0 hash.txt /usr/share/wordlists/rockyou.txt 秒破。hashlib.pbkdf2_hmac("sha256", password.encode(), salt, 600000) 计算哈希。密码强度检测可以把 rockyou.txt 读入集合,用 password in rockyou_set 判断。注意 rockyou.txt 有约 1400 万行,加载到内存约 300MB。