
密码学哈希函数详解 - SHA-256、SHA-512及更多
📷 Joppe Beurskens / Pexels密码学哈希函数详解 - SHA-256、SHA-512及更多
密码学哈希函数全面指南,涵盖SHA-256、SHA-512、MD5等。了解哈希特性及其在校验和、密码哈希、区块链和数字签名中的应用。
引言:什么是哈希函数?
哈希函数是一种数学函数,接受任意大小的输入并产生固定大小的输出,称为哈希值、摘要或校验和。密码学哈希函数增加了安全特性,使其在软件开发、网络安全、区块链技术和数据完整性验证中不可或缺。
哈希函数在现代计算中无处不在。每当你登录网站、验证文件下载、向Git提交代码或与区块链交互时,哈希函数都在幕后工作。理解不同哈希算法的工作原理和使用场景是每位开发者的必备知识。
想快速生成哈希?试试我们的哈希生成器工具,支持MD5、SHA-1、SHA-256、SHA-384和SHA-512。
密码学哈希函数的核心特性
优秀的密码学哈希函数必须具备几个关键特性,这些特性将其与简单校验和或普通哈希函数区分开来。
1. 确定性
相同的输入始终产生相同的输出。如果用SHA-256对字符串「Hello, World!」进行哈希运算,在任何机器、任何编程语言中都会得到完全相同的结果。
Input: "Hello, World!"
SHA-256: dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f
2. 固定长度输出
无论输入大小如何,输出长度始终相同:
| 算法 | 输出大小(位) | 输出大小(十六进制字符) |
|---|---|---|
| MD5 | 128 | 32 |
| SHA-1 | 160 | 40 |
| SHA-224 | 224 | 56 |
| SHA-256 | 256 | 64 |
| SHA-384 | 384 | 96 |
| SHA-512 | 512 | 128 |
| SHA-3-256 | 256 | 64 |
| BLAKE3 | 256(默认) | 64 |
Input: "a"
SHA-256: ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb
Input: "The quick brown fox jumps over the lazy dog"
SHA-256: d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592
Input: [1GB文件的全部内容]
SHA-256: [仍然恰好是64个十六进制字符]
3. 原像抗性
给定一个哈希值,应该在计算上不可能找到产生该哈希的输入。换句话说,哈希函数是单向函数——无法逆向运算。
Hash: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
你能找到输入吗?只能通过暴力破解。
(顺便说一下,答案是空字符串)
4. 第二原像抗性
给定一个输入及其哈希值,在计算上应不可能找到另一个产生相同哈希的输入。
5. 抗碰撞性
在计算上应不可能找到任何两个不同的输入产生相同的哈希输出。
6. 雪崩效应
输入的微小变化应导致输出的剧烈变化。即使只改变一个比特,也应改变大约一半的输出比特。
Input: "Hello, World!"
SHA-256: dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f
Input: "Hello, World?"(将!改为?)
SHA-256: 4a6e2ef4e4fa0e534f1d1989f453daa87f36e5e4e35b3dcb3d82f22d12654083
尽管输入只差一个字符,两个哈希值看起来完全不同。你可以用我们的哈希生成器亲自验证。
SHA哈希函数家族
安全哈希算法(SHA)家族是使用最广泛的密码学哈希函数集合。由美国国家安全局(NSA)开发,NIST发布。
SHA-1(已弃用)
SHA-1生成160位(20字节)的哈希值。曾是多年的标准,但现在被认为已被攻破,不应用于安全目的。
- 状态:已弃用——不要用于新应用
- 碰撞发现:2017年Google和CWI Amsterdam展示了实际碰撞(SHAttered攻击)
- 仍在使用:Git仍使用SHA-1作为对象ID(正在过渡到SHA-256)
- 输出:40个十六进制字符
SHA-256
SHA-256是当今使用最广泛的哈希函数。它是SHA-2家族的一部分,生成256位(32字节)的哈希值。
- 状态:安全且广泛推荐
- 用途:Bitcoin、TLS证书、代码签名、文件完整性检查
- 性能:中等——大多数应用足够快
- 输出:64个十六进制字符
// JavaScript: 生成SHA-256哈希
async function sha256(message) {
const encoder = new TextEncoder();
const data = encoder.encode(message);
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}
const hash = await sha256('Hello, World!');
console.log(hash);
// dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f
SHA-384
SHA-384是SHA-512的截断版本,生成384位的哈希值。它提供比SHA-256略高的安全余量。
- 状态:安全
- 用途:TLS、高安全性应用
- 输出:96个十六进制字符
SHA-512
SHA-512生成512位(64字节)的哈希值。在64位系统上,由于以更大的块处理数据,实际上比SHA-256更快。
- 状态:安全
- 性能:在64位系统上通常比SHA-256更快
- 用途:密码哈希、数字签名、高安全性应用
- 输出:128个十六进制字符
import hashlib
# Python中的SHA-512
message = "Hello, World!"
hash_object = hashlib.sha512(message.encode('utf-8'))
hex_digest = hash_object.hexdigest()
print(hex_digest)
# 374d794a95cdcfd8b35993185fef9ba368f160d8daf432d08ba9f1ed1e5abe6cc69291e0fa2fe0006a52570ef18c19def4e617c33ce52ef0a6e5fbe318cb0387
SHA-2家族对比
| 算法 | 输出位数 | 块大小 | 内部状态 | 安全级别 |
|---|---|---|---|---|
| SHA-224 | 224 | 512 | 256 | 112位 |
| SHA-256 | 256 | 512 | 256 | 128位 |
| SHA-384 | 384 | 1024 | 512 | 192位 |
| SHA-512 | 512 | 1024 | 512 | 256位 |
| SHA-512/224 | 224 | 1024 | 512 | 112位 |
| SHA-512/256 | 256 | 1024 | 512 | 128位 |
SHA-3(Keccak)
SHA-3是SHA家族的最新成员,基于Keccak算法。它使用与SHA-2根本不同的构造(海绵构造),提供算法多样性。
- 状态:安全,2015年标准化
- 构造:海绵构造(不同于SHA-2的Merkle-Damgard)
- 变体:SHA-3-224、SHA-3-256、SHA-3-384、SHA-3-512、SHAKE128、SHAKE256
- 用途:SHA-2万一被攻破时的备用方案
MD5:为什么它仍然存在
MD5生成128位的哈希值,曾经是最流行的哈希函数。现在在密码学上已经被攻破,但仍用于非安全目的。
不要将MD5用于:
- 密码哈希
- 数字签名
- 证书验证
- 任何安全关键应用
MD5仍可用于:
- 非密码学校验和(验证文件未被意外损坏)
- 内容寻址存储(不涉及安全的场景)
- 数据去重
- 旧系统兼容性
import hashlib
# MD5在文件校验和中仍然常见
md5_hash = hashlib.md5(b"Hello, World!").hexdigest()
print(md5_hash) # 65a8e27d8879283831b664bd8b7f0ad4
BLAKE3:现代替代方案
BLAKE3是一种较新的哈希函数,在保持强安全特性的同时,比SHA-2和SHA-3有显著的性能提升。
BLAKE3的优势:
- 极快:在现代CPU上比SHA-256快4-8倍
- 可并行:可利用SIMD指令和多核
- 安全:基于经过充分分析的BLAKE2和ChaCha流密码
- 多功能:可用作哈希、MAC、KDF和XOF
use blake3;
fn main() {
let hash = blake3::hash(b"Hello, World!");
println!("{}", hash.to_hex());
// 288a26b2bfb0602c0c7c9e4bf714f53f46c090da7e7ab8a30af9bf6c8e3bf0f8
}
哈希函数的应用场景
1. 文件完整性和校验和
哈希函数验证文件在传输过程中未被损坏或篡改。
# 生成校验和
sha256sum ubuntu-24.04.iso > checksum.txt
# 验证校验和
sha256sum -c checksum.txt
# ubuntu-24.04.iso: OK
// JavaScript中验证文件完整性
async function verifyFileIntegrity(file, expectedHash) {
const buffer = await file.arrayBuffer();
const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const actualHash = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
return actualHash === expectedHash;
}
使用我们的哈希生成器可以快速计算文件和字符串的校验和。
2. 密码哈希
以明文存储密码是严重的安全漏洞。哈希函数允许你在不存储密码的情况下进行验证。
**重要:**不要使用纯SHA-256进行密码哈希。请使用包含盐值且有意设计为低速的专用密码哈希函数。
推荐的密码哈希算法:
| 算法 | 状态 | 关键特性 |
|---|---|---|
| Argon2id | 最佳选择 | 内存密集型,抗GPU |
| bcrypt | 久经考验 | 广泛支持,经过实战检验 |
| scrypt | 良好 | 内存密集型 |
| PBKDF2 | 可接受 | NIST批准,广泛可用 |
// Node.js中使用bcrypt进行密码哈希
import bcrypt from 'bcrypt';
// 密码哈希
const saltRounds = 12;
const passwordHash = await bcrypt.hash('user_password', saltRounds);
// 结果: $2b$12$LJ3m6gEwO/fSFqCVXWLwOeR/dYtTVkRDCwoGLBE0Fg6voFEOB5viy
// 密码验证
const isValid = await bcrypt.compare('user_password', passwordHash);
// true
// Argon2id(新应用推荐)
import argon2 from 'argon2';
const hash = await argon2.hash('user_password', {
type: argon2.argon2id,
memoryCost: 65536, // 64MB
timeCost: 3,
parallelism: 4,
});
const valid = await argon2.verify(hash, 'user_password');
为什么纯哈希不够:
// 错误做法: "password123"的SHA-256哈希
ef92b778bafe771e89245b89ecbc08a44a4e166c06659911881f383d4473e94f
// 拥有彩虹表的攻击者可以立即逆向查找。
// 解决方案: 使用带盐值的专用密码哈希函数。
了解更多密码安全知识,请查看我们的密码安全指南,并使用密码生成器生成强密码。
3. 区块链和加密货币
哈希函数是区块链技术的基础。Bitcoin在其工作量证明共识机制中使用SHA-256。
import hashlib
import time
def mine_block(data, difficulty):
"""简单的工作量证明挖矿模拟"""
prefix = '0' * difficulty
nonce = 0
while True:
text = f'{data}{nonce}'
hash_result = hashlib.sha256(text.encode()).hexdigest()
if hash_result.startswith(prefix):
return nonce, hash_result
nonce += 1
# 以难度4挖掘区块(哈希必须以0000开头)
nonce, hash_value = mine_block("Block data here", 4)
print(f"Nonce: {nonce}")
print(f"Hash: {hash_value}")
# 哈希将以"0000..."开头
区块链中的哈希函数:
- 区块哈希:每个区块包含前一个区块的哈希,形成不可变链
- 默克尔树:交易以树形结构组织,每个节点是其子节点的哈希
- 挖矿:工作量证明要求找到使哈希值低于目标值的nonce
- 地址:加密货币地址由公钥哈希派生
4. 数字签名
数字签名使用哈希函数高效地签署消息。不是签署整个消息(可能非常大),而是对消息进行哈希运算并签署哈希值。
// 简化的数字签名流程
// 1. 对消息进行哈希
const messageHash = await sha256(message);
// 2. 用私钥签署哈希
const signature = await crypto.subtle.sign(
{ name: 'RSASSA-PKCS1-v1_5' },
privateKey,
encoder.encode(messageHash)
);
// 3. 验证:对消息进行哈希并检查签名
const verifyHash = await sha256(message);
const isValid = await crypto.subtle.verify(
{ name: 'RSASSA-PKCS1-v1_5' },
publicKey,
signature,
encoder.encode(verifyHash)
);
5. 内容寻址存储
Git、IPFS和Docker等系统使用哈希函数通过内容的哈希来寻址。这提供了去重、完整性验证和不可变性。
# Git使用SHA-1作为对象ID(正在过渡到SHA-256)
$ echo "Hello, World!" | git hash-object --stdin
8ab686eafeb1f44702738c8b0f24f2567c36da6d
# Docker层的内容寻址
$ docker inspect --format='{{.Id}}' my-image
sha256:abc123...
6. HMAC(基于哈希的消息认证码)
HMAC将哈希函数与密钥结合,同时提供数据完整性和认证。
// Node.js中的HMAC
import { createHmac } from 'crypto';
function generateHmac(message, secretKey) {
return createHmac('sha256', secretKey)
.update(message)
.digest('hex');
}
// Webhook签名验证
function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = generateHmac(payload, secret);
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
// API请求签名
const timestamp = Date.now().toString();
const body = JSON.stringify({ action: 'transfer', amount: 100 });
const signaturePayload = `${timestamp}.${body}`;
const signature = generateHmac(signaturePayload, API_SECRET);
fetch('https://api.example.com/transactions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Timestamp': timestamp,
'X-Signature': signature,
},
body: body,
});
7. 子资源完整性(SRI)
浏览器可以使用SRI哈希验证获取的资源(脚本、样式表)未被篡改。
{/* SRI: 浏览器在执行脚本前验证哈希 */}
<script
src="https://cdn.example.com/library.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous"
></script>
<link
rel="stylesheet"
href="https://cdn.example.com/styles.css"
integrity="sha256-5IxE/yBJ7PB5TbXxYqBCj9n5xM4mECft3p7/+Gkh1I="
crossorigin="anonymous"
/>
# 生成SRI哈希
echo -n "file contents" | openssl dgst -sha384 -binary | openssl base64 -A
# 或者: shasum -b -a 384 file.js | xxd -r -p | base64
8. 数据去重
哈希函数在存储系统中识别重复的数据块:
// 使用内容哈希的简单去重
class DeduplicatedStorage {
constructor() {
this.store = new Map(); // hash -> data
this.references = new Map(); // hash -> reference count
}
async put(data) {
const hash = await sha256(data);
if (!this.store.has(hash)) {
this.store.set(hash, data);
this.references.set(hash, 1);
} else {
this.references.set(hash, this.references.get(hash) + 1);
}
return hash;
}
get(hash) {
return this.store.get(hash);
}
}
哈希函数安全注意事项
已知漏洞
| 算法 | 状态 | 已知攻击 |
|---|---|---|
| MD5 | 已破解 | 碰撞攻击、实际伪造 |
| SHA-1 | 已破解 | SHAttered碰撞攻击(2017年) |
| SHA-256 | 安全 | 无已知实际攻击 |
| SHA-512 | 安全 | 无已知实际攻击 |
| SHA-3 | 安全 | 无已知实际攻击 |
| BLAKE3 | 安全 | 无已知实际攻击 |
如何选择合适的哈希函数
通用哈希(校验和、数据完整性):
- 安全关键场景用SHA-256
- 性能关键场景用BLAKE3
- 非安全校验和可用MD5
密码哈希:
- Argon2id(最佳)
- bcrypt(久经考验)
- 永远不要用纯SHA-256/SHA-512
数字签名:
- SHA-256或SHA-384
- 长期安全用SHA-512
区块链:
- SHA-256(Bitcoin,大多数链)
- Keccak-256(Ethereum)
HMAC:
- HMAC-SHA-256(标准)
- HMAC-SHA-512(更高安全余量)
长度扩展攻击
SHA-256和SHA-512(但不包括SHA-3或BLAKE3)容易受到长度扩展攻击。这意味着给定hash(message),攻击者可以在不知道原始消息的情况下计算hash(message || padding || extension)。
**缓解措施:**使用HMAC而非原始哈希函数进行认证。HMAC不受长度扩展攻击影响。
// 易受长度扩展攻击
const token = sha256(secret + message);
// 安全做法: 使用HMAC
const token = hmacSha256(secret, message);
哈希函数实现
JavaScript(浏览器和Node.js)
// 浏览器 Web Crypto API
async function hash(algorithm, data) {
const encoder = new TextEncoder();
const encoded = encoder.encode(data);
const hashBuffer = await crypto.subtle.digest(algorithm, encoded);
return Array.from(new Uint8Array(hashBuffer))
.map(b => b.toString(16).padStart(2, '0'))
.join('');
}
// 使用示例
await hash('SHA-256', 'Hello, World!');
await hash('SHA-384', 'Hello, World!');
await hash('SHA-512', 'Hello, World!');
// Node.js crypto模块
import { createHash } from 'crypto';
function hashNode(algorithm, data) {
return createHash(algorithm).update(data).digest('hex');
}
hashNode('sha256', 'Hello, World!');
hashNode('sha512', 'Hello, World!');
hashNode('md5', 'Hello, World!');
Python
import hashlib
# 所有主要算法
data = b"Hello, World!"
print("MD5: ", hashlib.md5(data).hexdigest())
print("SHA-1: ", hashlib.sha1(data).hexdigest())
print("SHA-256:", hashlib.sha256(data).hexdigest())
print("SHA-384:", hashlib.sha384(data).hexdigest())
print("SHA-512:", hashlib.sha512(data).hexdigest())
print("SHA3-256:", hashlib.sha3_256(data).hexdigest())
# 文件哈希
def hash_file(filepath, algorithm='sha256'):
h = hashlib.new(algorithm)
with open(filepath, 'rb') as f:
while chunk := f.read(8192):
h.update(chunk)
return h.hexdigest()
Go
package main
import (
"crypto/sha256"
"crypto/sha512"
"fmt"
)
func main() {
data := []byte("Hello, World!")
// SHA-256
sha256Hash := sha256.Sum256(data)
fmt.Printf("SHA-256: %x\n", sha256Hash)
// SHA-512
sha512Hash := sha512.Sum512(data)
fmt.Printf("SHA-512: %x\n", sha512Hash)
}
性能对比
不同哈希算法之间的性能差异显著。以下是在现代硬件上的大致基准:
| 算法 | 速度(MB/s) | 相对速度 |
|---|---|---|
| MD5 | 约700 | 1.4倍 |
| SHA-1 | 约550 | 1.1倍 |
| SHA-256 | 约500 | 1.0倍(基准) |
| SHA-512 | 约600 | 1.2倍(64位系统) |
| SHA-3-256 | 约400 | 0.8倍 |
| BLAKE3 | 约3000+ | 6.0倍以上 |
注意:BLAKE3的性能优势来自其利用SIMD指令和并行性的能力。实际性能取决于硬件和实现。
总结
密码学哈希函数是现代计算和安全的基础构建块。理解不同哈希算法的特性、优势和适用场景,对于从事安全、数据完整性或分布式系统工作的每位开发者都至关重要。
核心要点:
- SHA-256是安全的默认选择——满足大多数密码学哈希需求
- 永远不要将MD5或SHA-1用于安全目的
- 使用专用密码哈希(Argon2id、bcrypt)——而非纯哈希函数
- HMAC防止长度扩展攻击——用于认证场景
- BLAKE3是性能之王——需要速度时的首选
- 雪崩效应是你的朋友——微小的输入变化产生完全不同的哈希
- 抗碰撞性很重要——选择没有已知实际碰撞的算法
要快速生成和比较哈希,请收藏我们的哈希生成器工具。它支持MD5、SHA-1、SHA-256、SHA-384和SHA-512,可以直接在浏览器中轻松计算和验证哈希。
相关资源
- 哈希生成器 -- 即时生成哈希
- 密码安全指南 -- 安全密码实践
- 密码生成器 -- 生成强密码
- API安全最佳实践 -- API安全中的哈希
- Base64编码详解 -- 编码与哈希的区别