hash sha256 sha512 crypto

必备加密哈希生成器:SHA-256, SHA-512 及更多

在数秒内生成安全的加密哈希(如 SHA-256 和 SHA-512)。保障数据完整性的必备工具。

密码学哈希函数:完整指南

密码学哈希函数是现代安全体系中默默无闻的基石。每当你登录网站、向 Git 推送代码、下载文件,或者发起一笔比特币交易,哈希函数都在幕后默默运转。然而,大多数开发者每天都在与它们打交道,却并不真正理解它们的原理——也不清楚当使用不当时会出现多大的安全漏洞。

本指南涵盖了一切内容:数学原理、发展历史、已被破解的算法、现代标准,以及正确使用哈希函数所需的实用代码。


1. 什么是密码学哈希函数?

密码学哈希函数接受任意大小的输入,并产生一个固定大小的输出,称为摘要或哈希值。例如,无论输入是单个字符还是整部电影文件,SHA-256 始终输出 256 位(64 个十六进制字符)。

核心属性

确定性:相同的输入总是产生相同的输出。SHA-256("hello") 永远返回 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824

计算快速:计算哈希值应该只需要毫秒级时间。这种高效性对文件完整性校验和数字签名至关重要,尽管对于密码哈希来说却成了一个弱点(后文会详细介绍)。

单向性(预像抗性):给定哈希输出 H,在计算上不可行找到任何输入 m,使得 hash(m) = H。你无法仅从哈希值逆向推导出原始数据。

第二预像抗性:给定输入 m1,在计算上不可行找到不同的输入 m2,使得 hash(m1) = hash(m2)。即使攻击者知道你的原始数据,也无法找到产生相同哈希值的不同输入。

碰撞抗性:在计算上不可行找到任意两个不同输入 m1m2,使得 hash(m1) = hash(m2)。这比第二预像抗性要求更强。

雪崩效应:输入的微小变化——哪怕只改变一个比特——会导致输出完全不同。将 "hello" 改为 "hellp" 会产生与原来没有任何表面关联的完全不同的哈希值。

SHA-256("hello") = 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
SHA-256("hellp") = 9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca7

这两个哈希值之间没有可预测的关联——这就是雪崩效应的体现。


2. 哈希算法简史

MD5(1991 年)

Ronald Rivest 设计了 MD5,作为 MD4 的改进版本。它产生 128 位摘要,在整个 20 世纪 90 年代被广泛用于校验和与密码存储。在超过十年的时间里,MD5 是许多安全应用的默认选择。

SHA-1(1995 年)

美国国家安全局(NSA)将 SHA-1(安全哈希算法 1)设计为数字签名标准的一部分。它产生 160 位摘要。SHA-1 成为 TLS/SSL 证书、代码签名以及 Git 对象存储的主导哈希算法。

SHA-2 家族(2001 年)

同样由 NSA 设计,SHA-2 实际上是六个函数的家族:SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224 和 SHA-512/256。SHA-256 和 SHA-512 是最常用的,分别产生 256 位和 512 位摘要,至今仍然安全。

SHA-3 / Keccak(2015 年)

在 SHA-1 的弱点变得明显之后,美国国家标准与技术研究院(NIST)举办了一场公开竞赛(2007–2015 年),寻找完全独立于 NSA 的 SHA-2 设计的全新哈希标准。获胜者是 Keccak,由 Guido Bertoni、Joan Daemen、Michaël Peeters 和 Gilles Van Assche 设计。与使用 Merkle–Damgård 构造的 SHA-2 不同,SHA-3 使用海绵构造,提供了根本不同的安全特性。

BLAKE2(2012 年)

BLAKE2 是一种密码学哈希函数,速度比 MD5 更快,同时提供 SHA-3 级别的安全性。它由 Jean-Philippe Aumasson、Samuel Neves、Zooko Wilcox-O'Hearn 和 Christian Winnerlein 设计,是 SHA-3 竞赛决赛入围者 BLAKE 的改进版本。BLAKE2b 针对 64 位平台优化;BLAKE2s 针对 32 位平台优化。


3. SHA-256 的工作原理

SHA-256 使用 Merkle–Damgård 构造:消息被分割成固定大小的块,压缩函数被迭代应用,将一个块的输出作为下一个块的输入。

第一步:填充

输入消息被填充,使其总长度是 512 位的倍数。具体方法是追加一个 1 比特,然后是若干个零,最后是原始消息长度的 64 位大端整数表示。

第二步:消息调度

每个 512 位的块被扩展为 64 个 32 位字,使用一个混合和旋转比特的调度方法。W[0]W[15] 直接来自消息块;W[16]W[63] 按如下方式计算:

W[i] = σ1(W[i-2]) + W[i-7] + σ0(W[i-15]) + W[i-16]

其中 σ0 和 σ1 是特定的比特旋转和移位操作。

第三步:压缩——64 轮运算

SHA-256 维护 8 个工作变量(a 到 h),初始化为前 8 个素数平方根的小数部分。对于 64 轮中的每一轮,算法应用:

T1 = h + Σ1(e) + Ch(e,f,g) + K[i] + W[i]
T2 = Σ0(a) + Maj(a,b,c)
h = g; g = f; f = e; e = d + T1
d = c; c = b; b = a; a = T1 + T2

轮常数 K[i] 是前 64 个素数立方根的小数部分——这一设计选择通过使常数可公开验证,防止了"袖中藏物"的批评,证明其中没有隐藏后门。

第四步:输出

处理完所有块后,将 8 个工作变量与初始哈希值相加,产生最终的 256 位摘要。这种"前馈"机制确保每个块的输出都依赖于所有之前的块。


4. MD5 和 SHA-1 为何已被破解

MD5 碰撞(2004 年)

2004 年,王小云等人证明了对 MD5 的实际碰撞攻击——找到两个不同的输入产生相同的 MD5 哈希值。到 2008 年,研究人员利用 MD5 碰撞伪造了来自真实 CA 的虚假 SSL 证书,展示了对 HTTPS 基础设施的现实世界攻击。

该攻击使用复杂的差分密码分析,可以在现代硬件上几秒内生成 MD5 碰撞。

SHA-1 "SHAttered" 攻击(2017 年)

谷歌 Project Zero 团队和荷兰数学与计算机科学研究学会(CWI Amsterdam)在 2017 年产生了第一个实际的 SHA-1 碰撞,被称为 SHAttered。他们生成了两个具有相同 SHA-1 哈希值的不同 PDF 文件。该攻击需要大约 9.2 × 10¹⁸ 次 SHA-1 计算——相当于单 CPU 运行 6500 年,但只相当于 GPU 运行约 110 年,这对于民族国家和大型组织来说完全可行。

实际影响

MD5 和 SHA-1 对于以下用途不安全

  • 数字签名
  • 证书指纹
  • 密码存储
  • 任何安全敏感的应用

对于以下用途仍然可接受

  • 非密码学校验和(通过可信渠道验证文件下载完整性)
  • 哈希表查找
  • 非安全性去重
  • 遗留系统兼容性(附带适当的注意事项)

5. 真实世界中的使用场景

密码存储

绝不要以明文存储密码——甚至不能以普通哈希形式存储。如果你的数据库被泄露,攻击者可以在数小时或数天内使用字典攻击或彩虹表破解普通哈希。

正确的方法是使用专为密码设计的慢速加盐哈希函数:bcrypt、scrypt 或 Argon2。

文件完整性验证

当你下载软件时,开发者会提供一个 SHA-256 校验和。下载后,你计算文件的哈希值并进行比较。如果匹配,说明文件在传输过程中没有被损坏或篡改。

sha256sum downloaded-file.tar.gz
# 与开发者发布的校验和进行比较

数字签名

哈希函数是数字签名的基础。与其对整个文档(可能有几个 GB)进行签名,不如对其哈希值进行签名。接收方独立地对文档进行哈希,并对照该哈希值验证签名。

区块链

比特币对工作量证明挖矿和对交易块的哈希使用 SHA-256 双重哈希(SHA-256d)。矿工必须找到一个输入(随机数),使其哈希后产生带有特定数量前导零的输出——这个过程需要巨大的计算工作量,提供了区块链的安全保证。

Git 对象存储

Git 使用 SHA-1 对每个提交、树和 blob 对象进行哈希。哈希值既作为对象的标识符,也作为完整性检查。由于 SHA-1 的弱点,Git 正在积极迁移到 SHA-256。

存储系统去重

备份系统和内容寻址存储(如 IPFS)使用哈希来识别重复内容。如果两个文件具有相同的哈希值,它们只存储一次。


6. 实际计算哈希值

JavaScript (Node.js)

const crypto = require('crypto');

// SHA-256
const sha256 = crypto.createHash('sha256')
  .update('Hello, World!')
  .digest('hex');
console.log(sha256);
// 315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3

// MD5(仅用于非安全目的)
const md5 = crypto.createHash('md5')
  .update('Hello, World!')
  .digest('hex');
console.log(md5);
// 65a8e27d8879283831b664bd8b7f0ad4

// SHA-512
const sha512 = crypto.createHash('sha512')
  .update('Hello, World!')
  .digest('hex');
console.log(sha512);

Python

import hashlib

# SHA-256
h = hashlib.sha256(b"Hello, World!").hexdigest()
print(h)
# 315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3

# SHA-512
h512 = hashlib.sha512(b"Hello, World!").hexdigest()
print(h512)

# 多种算法
for algo in ['md5', 'sha1', 'sha256', 'sha512']:
    h = hashlib.new(algo, b"Hello, World!").hexdigest()
    print(f"{algo}: {h}")

Bash / Shell

# SHA-256
echo -n "Hello, World!" | sha256sum
# 315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3  -

# MD5
echo -n "Hello, World!" | md5sum
# 65a8e27d8879283831b664bd8b7f0ad4  -

# SHA-1
echo -n "Hello, World!" | sha1sum

# 对文件进行哈希
sha256sum /path/to/file.iso

7. HMAC:基于哈希的消息认证码

普通哈希只验证数据完整性——它告诉你数据是否被损坏。但它不验证真实性——它不能证明是谁创建了数据。任何人都可以计算哈希值。

HMAC(RFC 2104)通过将密钥与哈希函数结合来解决这个问题:

HMAC(K, m) = hash((K' ⊕ opad) || hash((K' ⊕ ipad) || m))

其中 K' 是填充到块大小的密钥,opad/ipad 是特定的填充常数。如果底层哈希函数是安全的,这种构造就是可证明安全的。

常见用途

API 认证:REST API 使用 HMAC-SHA256 对请求进行签名。服务器和客户端共享一个密钥。客户端用密钥对请求体进行签名;服务器验证签名。

JWT 签名:JSON Web Token 使用 HMAC-SHA256(HS256)对头部和载荷进行签名,确保令牌未被篡改。

Webhook 验证:GitHub、Stripe 和许多其他服务使用 HMAC-SHA256 对 Webhook 载荷进行签名,使接收者可以验证载荷的真实性。

计算 HMAC

// Node.js
const crypto = require('crypto');

const hmac = crypto.createHmac('sha256', '我的密钥')
  .update('需要认证的消息')
  .digest('hex');
console.log(hmac);
import hmac
import hashlib

key = b'my-secret-key'
message = b'message to authenticate'
sig = hmac.new(key, message, hashlib.sha256).hexdigest()
print(sig)

8. 彩虹表与加盐

什么是彩虹表?

彩虹表是一个预计算的数据库,将已知的哈希值映射回其原始明文输入。如果攻击者获得了你的密码哈希数据库,他们不需要逐个破解每个哈希——只需在表中查找即可。

对于 MD5 和 SHA-1,涵盖所有长度不超过 8 个字符的 ASCII 密码的彩虹表多年来已经可以免费获取。CrackStation 等网站维护着数十亿个哈希到密码的映射数据库。

加盐如何击败彩虹表

是在哈希之前附加到密码的随机值:

hash(salt + password) = stored_hash

盐与哈希值一起存储(它不需要保密)。因为每个用户都得到一个唯一的随机盐,攻击者无法使用预计算的表——他们需要为每个可能的盐值单独建立彩虹表,这在计算上是不可能的。

bcrypt:自动加盐与刻意的慢速

bcrypt 于 1999 年专门为密码哈希而设计。它自动生成并整合随机盐,并包含一个控制哈希计算速度的代价因子

const bcrypt = require('bcrypt');

// 对密码进行哈希(代价因子 12——在现代硬件上约需 250ms)
const hash = await bcrypt.hash('用户密码', 12);

// 验证
const isMatch = await bcrypt.compare('用户密码', hash);

存储的哈希值如下:$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW

$2b$12$ 前缀编码了算法版本和代价因子——bcrypt 自动处理一切。


9. 算法对比表

算法 输出大小 速度 安全状态 最适合
MD5 128 位 非常快 ❌ 已破解(碰撞) 仅用于非安全校验和
SHA-1 160 位 ❌ 已破解(SHAttered) 仅用于遗留系统
SHA-256 256 位 ✅ 安全 通用、TLS、签名
SHA-512 512 位 64 位平台上快 ✅ 安全 高安全性应用
SHA-3/Keccak 可变 中等 ✅ 安全 SHA-2 的替代方案
BLAKE2b 可变 非常快 ✅ 安全 性能关键型哈希
bcrypt 184 位 慢(刻意) ✅ 安全 密码存储
Argon2id 可变 慢(刻意) ✅ 安全 密码存储(推荐)

10. 密码哈希最佳实践

密码需要特殊处理,因为它们是用户账户的钥匙。密码数据库泄露可能造成灾难性后果。毫无例外地遵循以下规则:

规则一:绝不存储明文密码

这应该是显而易见的,但仍然有人这样做。2019 年,Facebook 被发现在内部以明文形式存储了数亿个密码。

规则二:绝不对密码使用快速哈希

MD5、SHA-1、SHA-256 和 SHA-512 对密码哈希来说太快了。现代 GPU 每秒可以计算数十亿次 SHA-256 哈希,使暴力破解攻击在数小时内成为可能。

规则三:使用专为密码设计的哈希算法

bcrypt(推荐最低标准):使用代价因子 12 或更高。广泛支持,久经考验。

scrypt:内存困难型,对 GPU 和 ASIC 攻击具有抵抗力。可配置内存和 CPU 成本。

Argon2id(当今推荐):2015 年密码哈希竞赛的获胜者。Argon2id 是推荐的变体,因为它既能抵抗侧信道攻击,又能抵抗时间-内存权衡攻击。最低配置:

  • 内存:64 MB
  • 迭代次数:3
  • 并行度:4

规则四:每个密码使用唯一的盐

即使使用了 bcrypt/scrypt/Argon2(它们包含自动加盐),也要理解其重要性:相同的密码必须产生不同的哈希值,这样即使一个密码被破解,也不会暴露其他密码。

规则五:随时间调整代价因子

随着硬件变得更快,提高代价因子。对于 bcrypt,目标是约 250–500ms。在下次登录时重新哈希密码。

规则六:考虑使用胡椒(Pepper)

胡椒是一个服务器端密钥(与盐不同,它不存储在数据库中)。它在哈希之前添加到密码中:hash(pepper + salt + password)。即使攻击者盗取了你的数据库,没有胡椒也无法破解密码。


结论

密码学哈希函数是互联网安全、完整性和信任的基础。理解它们——从数学属性到实际漏洞——能够帮助你构建真正安全的系统。

核心要点:

  • SHA-256 和 SHA-512 是通用哈希的首选
  • MD5 和 SHA-1 在密码学用途上已被破解
  • 对于密码,始终使用 bcrypt、scrypt 或 Argon2
  • 当你需要认证而不仅仅是完整性验证时,使用 HMAC
  • 加盐能够击败彩虹表;bcrypt 和 Argon2 会自动完成加盐

使用 Tool3M 哈希生成器,直接在浏览器中快速计算 SHA-256、SHA-512、MD5 等哈希值——无需安装任何软件。