常见Hash算法

部分内容来源:JavaGuide


什么是Hash算法

哈希算法也叫散列函数或摘要算法,它的作用是对任意长度的数据生成一个固定长度的唯一标识

也叫哈希值、散列值或消息摘要

哈希算法的是不可逆的,你无法通过哈希之后的值再得到原值

哈希值的作用是可以用来验证数据的完整性和一致性

哈希算法可以简单分为两类:

  1. 加密哈希算法:安全性较高的哈希算法,它可以提供一定的数据完整性保护和数据防篡改能力,能够抵御一定的攻击手段,安全性相对较高,但性能较差,适用于对安全性要求较高的场景。例如 SHA2、SHA3、SM3、RIPEMD-160、BLAKE2、SipHash 等等
  2. 非加密哈希算法:安全性相对较低的哈希算法,易受到暴力破解、冲突攻击等攻击手段的影响,但性能较高,适用于对安全性没有要求的业务场景。例如 CRC32、MurMurHash3、SipHash 等等。、

除了这两种之外,还有一些特殊的哈希算法,例如安全性更高的慢哈希算法

哈希算法一般是不需要密钥的,但也存在部分特殊哈希算法需要密钥。例如,MAC 和 SipHash 就是一种基于密钥的哈希算法,它在哈希算法的基础上增加了一个密钥,使得只有知道密钥的人才能验证数据的完整性和来源


什么是彩虹表

彩虹表 是一种预先计算好的 哈希值与明文密码的映射表,用于快速破解哈希算法加密的密码

它通过 时间-存储权衡大幅减少暴力破解所需的时间

尤其对弱密码或未加盐(Unsalted)的哈希攻击效果极强


1. 彩虹表的工作原理

(1)普通暴力破解 vs 彩虹表

方法

过程

缺点

暴力破解

逐个尝试所有可能的密码组合(如 a→b→...→zzzz

),计算哈希并比对

速度极慢,需实时计算所有可能性

彩虹表

提前计算并存储大量「明文→哈希」的映射,破解时直接查表

占用存储空间,但破解速度极快

(2)彩虹表的优化技术

减少存储量:不直接存储所有明文-哈希对,而是通过 哈希链(Hash Chain) 压缩数据

例如:明文1 → 哈希1 → 明文2 → 哈希2 → ... → 明文N → 哈希N,只存储链的起点和终点

碰撞还原:通过链式推导,从哈希值反向还原明文(需多次计算)


2. 彩虹表的攻击场景

(1)成功条件

目标密码的哈希值 未加盐(No Salt)

密码本身 强度低(如常见单词、短密码)

(2)典型受害算法

MD5SHA-1 等快速哈希算法(计算速度快,易生成彩虹表)

数据库泄露的密码哈希(如 password_hash = md5("123456")

(3)实际案例

早年 LinkedIn 密码泄露事件(600万密码用 SHA-1 未加盐存储,被彩虹表秒破)

Wi-Fi WPA2 破解:用彩虹表加速 PSK(预共享密钥)破解


3. 如何防御彩虹表攻击?

(1)加盐(Salt)

在密码哈希前,拼接一个 随机盐值(每个用户不同):

salt = random_bytes(16)  # 随机生成16字节盐
hash = sha256(salt + password)

效果:即使相同密码,哈希结果也不同,使彩虹表失效

(2)使用慢哈希算法

选择 bcryptArgon2PBKDF2 等故意降低计算速度的算法。

例如:bcrypt.hashpw(password, bcrypt.gensalt())

(3)密钥拉伸(Key Stretching)

通过多次迭代(如 10万次哈希)增加计算成本:

pythonhash = pbkdf2_hmac('sha256', password, salt, 100000)
(4)密码策略

强制用户使用 长密码+特殊字符(如 MyP@ssw0rd!2023

禁用常见弱密码(如 123456password


4. 彩虹表 vs 其他攻击方式

攻击方式

特点

防御措施

彩虹表

依赖预计算表,对未加盐哈希高效

加盐 + 慢哈希

暴力破解

逐个尝试所有组合,速度慢

增加密码复杂度 + 限速尝试

字典攻击

尝试常见单词和变种

禁用常见密码

GPU/ASIC 破解

硬件加速暴力破解

使用抗硬件算法(如 Argon2)


5. 彩虹表现状

逐渐失效:现代系统普遍采用 加盐+慢哈希,彩虹表难以直接使用

仍存威胁:对老旧系统或错误配置(如未加盐的 MD5)依然有效

最佳实践

永远使用 加盐的 bcrypt/Argon2 存储密码

定期审计系统中的哈希算法(禁用 MD5/SHA-1)


加密Hash

加密哈希算法主要用于 安全敏感场景,如密码存储、数字签名、数据完整性校验等

它们必须满足严格的密码学特性

核心特性

  1. 抗碰撞性(Collision Resistance)

极难找到两个不同的输入 x ≠ y,使得 Hash(x) = Hash(y)

例如:SHA-256、SHA-3、BLAKE3

  1. 单向性(Pre-image Resistance)

给定哈希值 h,难以反推出原始输入 x(即 Hash(x) = h

例如:MD5(已不安全)、SHA-1(已不安全)、SHA-256


常见加密哈希算法

算法

输出长度(bit)

安全性状态

典型用途

MD5

128

❌ 已破解(碰撞攻击)

文件校验(非安全场景)

SHA-1

160

❌ 已破解(碰撞攻击)

旧版 TLS/SSL(已弃用)

SHA-256

256

✅ 安全

比特币、密码存储

SHA-3

可变(224/256/384/512)

✅ 安全

替代 SHA-2

BLAKE3

可变(默认 256)

✅ 安全

高性能哈希


典型应用

  • 密码存储(加盐哈希,如 PBKDF2bcryptArgon2
  • 区块链(比特币使用 SHA-256)
  • 数字签名(如 RSA + SHA-256)
  • 文件完整性校验(如下载文件的 SHA-256 校验和)

非加密Hash

非加密哈希算法 不追求密码学安全性,而是专注于 高性能低碰撞率

适用于哈希表、缓存、数据分片等场景

核心特点

  1. 计算速度快(比加密哈希快 10-100 倍)
  2. 不保证抗碰撞性(可能被恶意构造碰撞)
  3. 不保证单向性(可能被逆向工程)

常见非加密哈希算法

算法

特点

典型用途

MurmurHash

高性能,适合短文本

Redis、HashMap

xxHash

极快,适合大文件

数据分片、校验

CityHash

Google 优化,适合字符串

大数据处理

FNV-1a

简单,低碰撞

数据库索引、缓存键


典型应用

  • 哈希表(如 Java 的 HashMap 使用 hashCode()
  • 分布式系统(如一致性哈希 Consistent Hashing
  • 缓存键生成(如 Redis 的键哈希)
  • 数据分片(如数据库分库分表)

慢Hash算法

慢哈希算法是专门设计用于 密码存储 的安全哈希算法

其核心特点是 故意降低计算速度,以抵御暴力破解和彩虹表攻击

这类算法通常结合加盐多次迭代,大幅增加攻击者的破解成本


慢Hash的设计目的

让合法用户验证密码时稍微慢一点,但让攻击者破解密码的成本高到无法承受


为什么需要慢哈希?

普通哈希算法(如 SHA-256、MD5)虽然安全,但 计算速度过快,攻击者可通过以下方式快速破解:

  1. 暴力破解:尝试数百万/秒的密码组合
  2. 彩虹表攻击:预计算哈希值与明文密码的映射表

慢哈希通过 人为引入计算延迟(如 100ms/次),使得攻击者需要数年甚至更长时间才能破解一个密码


常见慢哈希算法

1. PBKDF2(Password-Based Key Derivation Function 2)

原理:基于 HMAC(如 HMAC-SHA256)的多次迭代(通常 10万+ 次)

特点

  1. 可配置迭代次数(抗硬件加速破解)
  2. 需要加盐(防止彩虹表攻击)

import hashlib, binascii, os

salt = os.urandom(16)  # 随机盐
password = "user_password".encode()
hash = hashlib.pbkdf2_hmac('sha256', password, salt, 100000)
print(binascii.hexlify(hash).decode())

2. bcrypt

原理:基于 Blowfish 加密算法,引入 工作因子(Work Factor) 控制计算成本

特点

  1. 自适应:可随硬件性能提升增加工作因子
  2. 内置盐值

import bcrypt

password = "user_password".encode()
salt = bcrypt.gensalt(rounds=12)  # 工作因子=12(2^12 次迭代)
hash = bcrypt.hashpw(password, salt)
print(hash.decode())

3. Argon2(2015 年密码哈希竞赛冠军)

原理:同时消耗 CPU 和内存资源,抗 GPU/ASIC 破解

变种

  1. Argon2i:抗侧信道攻击(适合密码存储)
  2. Argon2d:抗 GPU 破解(适合加密货币)

from argon2 import PasswordHasher

ph = PasswordHasher(
    time_cost=3,      # 迭代次数
    memory_cost=65536, # 内存消耗(KB)
    parallelism=4     # 并行线程数
)
hash = ph.hash("user_password")
print(hash)

慢哈希 vs 普通哈希

特性

慢哈希(如 bcrypt)

普通哈希(如 SHA-256)

计算速度

故意慢(100ms~1s/次)

极快(微秒级)

抗暴力破解

✅ 大幅增加攻击成本

❌ 易被暴力破解

抗彩虹表

✅ 必须加盐

❌ 需额外加盐

适用场景

密码存储

数据完整性校验、数字签名


如何选择合适的慢哈希?

  1. 推荐优先级

Argon2 > bcrypt > PBKDF2(Argon2 是当前最安全的方案)

  1. 参数配置建议

PBKDF2:迭代次数 ≥ 100,000 次

bcrypt:工作因子 ≥ 12

Argon2time_cost≥3memory_cost≥64MBparallelism≥4


实际应用案例

1. 密码存储(数据库字段)
CREATE TABLE users (
    id INT PRIMARY KEY,
    username VARCHAR(50),
    password_hash VARCHAR(100),  -- 存储 bcrypt/Argon2 哈希值
    salt VARCHAR(50)             -- PBKDF2 需单独存盐
);
2. 用户注册/登录流程
# 注册时哈希密码
def register(username, password):
    salt = generate_salt()
    hash = argon2.hash(password + salt)
    db.save(username, hash, salt)

# 登录时验证
def login(username, password):
    user = db.get_user(username)
    if argon2.verify(password + user.salt, user.hash):
        return "Login success!"

总结

  1. 慢哈希的核心目标:通过 降低计算速度增加资源消耗,抵御密码破解
  2. 永远不要用 MD5/SHA-1 存储密码,优先选择 Argon2bcrypt
  3. 必须加盐:防止彩虹表攻击,即使相同密码哈希结果也不同

加盐Hash

加盐 Hash 是一种密码安全存储技术,通过在原始密码 拼接随机数据(Salt,盐值) 后再进行哈希计算,使得即使相同的密码也会生成不同的哈希值,从而有效防御 彩虹表攻击批量破解


为什么需要加盐?

问题:直接存储哈希值的风险

如果直接存储 Hash(password),攻击者可以通过以下方式破解:

  1. 彩虹表攻击:预计算常见密码的哈希值,直接反向查询
  2. 批量破解:同一密码的哈希值相同,破解一个等于破解所有

示例(不安全存储)

用户名

密码(MD5)

Alice

5f4dcc3b5aa765d61d8327deb882cf99

(password)

Bob

5f4dcc3b5aa765d61d8327deb882cf99

(password)

攻击者发现 Alice 和 Bob 的哈希相同,直接破解一次即可获取两人密码


解决方案:加盐(Salt)
  1. 盐值(Salt):一个 随机生成 的字符串(通常 16-32 字节),每个用户不同
  2. 存储方式:盐值 + 哈希值一起存入数据库

加盐后的存储示例

用户名

盐值(Salt)

密码哈希(Salt + Password)

Alice

a1b2c3...

(随机)

sha256("a1b2c3...password")

x1y2z3...

Bob

d4e5f6...

(随机)

sha256("d4e5f6...password")

p7q8r9...

即使密码相同,哈希值也不同,攻击者必须逐个破解!


加盐 Hash 的实现步骤

(1)用户注册时

生成随机盐值(每个用户唯一):

import os
salt = os.urandom(16)  # 生成 16 字节随机盐

计算加盐哈希

import hashlib
password = "user_password".encode()
hash = hashlib.sha256(salt + password).hexdigest()  # 加盐哈希

存储盐值和哈希

INSERT INTO users (username, salt, password_hash) 
VALUES ('alice', 'a1b2c3...', 'x1y2z3...');

(2)用户登录时

从数据库取出盐值和哈希

sqlSELECT salt, password_hash FROM users WHERE username = 'alice';

验证密码

input_password = "user_input".encode()
computed_hash = hashlib.sha256(salt + input_password).hexdigest()
if computed_hash == stored_hash:
    print("登录成功!")

加盐的核心作用

攻击方式

无盐哈希的风险

加盐哈希的防御效果

彩虹表攻击

直接查表破解

盐值使预计算表失效,必须重新生成

批量破解

破解一个哈希等于破解所有相同密码

每个用户的哈希不同,必须逐个破解

字典攻击

快速尝试常见密码

盐值增加密码复杂性,降低破解效率


加盐的最佳实践

  1. 盐值必须是唯一的
  2. 盐值长度必须足够长,避免盐值碰撞
  3. 盐值需与密码hash值一起存
  4. 结合慢hash算法,单独加盐仍存在暴力破解的可能,建议配置bcrypt/Argon2/PBKDF2

为什么单独加盐仍然存在破解的可能

即使使用了唯一且随机的盐值,如果哈希算法本身速度过快(如 SHA-256、MD5),攻击者仍可以:

  • 借助高性能硬件(GPU/ASIC)快速计算数十亿次哈希,尝试暴力破解。
  • 针对每个用户单独攻击(虽然盐值增加了成本,但不足以彻底阻止)

加盐 vs 其他安全措施

技术

作用

加盐的必要性

加盐(Salt)

防止彩虹表攻击和批量破解

✅ 必须

慢哈希

增加暴力破解成本

✅ 建议配合使用

密钥拉伸

通过多次迭代增加计算时间

✅ 建议配合使用


实际代码示例

(1)SHA-256(手动加盐+快速Hash算法)
import os
import hashlib

def hash_password(password):
    salt = os.urandom(16)  # 生成随机盐
    hash = hashlib.sha256(salt + password.encode()).hexdigest()
    return salt.hex(), hash  # 返回盐和哈希

salt, hash = hash_password("my_secure_password")
print(f"Salt: {salt}, Hash: {hash}")
(2)bcrypt-(自动加盐+慢Hash算法)

bcrypt可以从hash后字符串自动提取盐值,这样子用户下一次登陆时候的原始密码就能结合原始盐值进行hash运算得到原来的hash值

import bcrypt

password = "my_secure_password".encode()
hash = bcrypt.hashpw(password, bcrypt.gensalt())  # 自动加盐+慢哈希
print(f"Hash: {hash.decode()}")

总结

  1. 加盐 Hash = 密码 + 随机盐值 → 哈希计算
  2. 核心作用:防御彩虹表攻击,确保相同密码哈希不同
  3. 必须结合:随机盐(≥16 字节) + 慢哈希算法(如 bcrypt)
  4. 用户盐分唯一:表中存储用户唯一的盐分,方便下一次查询出来的时候拼接在Hash判断密码正确性
  5. 记住md5(password) ❌ → bcrypt(salt + password)

MD算法

MD 算法有多个版本,包括 MD2、MD4、MD5 等,其中 MD5 是最常用的版本,它可以生成一个 128 位(16 字节)的哈希值

从安全性上说:MD5 > MD4 > MD2。除了这些版本,还有一些基于 MD4 或 MD5 改进的算法,如 RIPEMD、HAVAL 等

即使是最安全 MD 算法 MD5 也存在被破解的风险,攻击者可以通过暴力破解或彩虹表攻击等方式,找到与原始数据相同的哈希值,从而破解数据

为了增加破解难度,通常可以选择加盐

盐(Salt)在密码学中,是指通过在密码任意固定位置插入特定的字符串,让哈希后的结果和使用原始密码的哈希结果不相符,这种过程称之为“加盐”

加盐之后就安全了吗?并不一定,这只是增加了破解难度,不代表无法破解。而且,MD5 算法本身就存在弱碰撞(Collision)问题,即多个不同的输入产生相同的 MD5 值

因此,MD 算法已经不被推荐使用,建议使用更安全的哈希算法比如 SHA-2、Bcrypt。

Java 提供了对 MD 算法系列的支持,包括 MD2、MD5

MD5 代码示例(未加盐):


String originalString = "KIRA";

// 创建MD5摘要对象
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.update(originalString.getBytes(StandardCharsets.UTF_8));

// 计算哈希值
byte[] result = messageDigest.digest();

// 将哈希值转换为十六进制字符串
String hexString = new HexBinaryAdapter().marshal(result);

System.out.println("Original String: " + originalString);
System.out.println("MD5 Hash: " + hexString.toLowerCase());

输出:

Original String: KIRA
MD5 Hash: fb246796f5b1b60d4d0268c817c608fa

SHA算法

SHA(Secure Hash Algorithm)系列算法是一组密码哈希算法

将任意长度的数据映射为固定长度的哈希值

SHA 系列算法由美国国家安全局(NSA)于 1993 年设计,目前共有 SHA-1、SHA-2、SHA-3 三种版本。

SHA-1 算法将任意长度的数据映射为 160 位的哈希值。然而,SHA-1 算法存在一些严重的缺陷,比如安全性低,容易受到碰撞攻击和长度扩展攻击。

因此,SHA-1 算法已经不再被推荐使用。

SHA-2 家族(如 SHA-256、SHA-384、SHA-512 等)和 SHA-3 系列是 SHA-1 算法的替代方案,它们都提供了更高的安全性和更长的哈希值长度

SHA-2 家族是在 SHA-1 算法的基础上改进而来的,它们采用了更复杂的运算过程和更多的轮次,使得攻击者更难以通过预计算或巧合找到碰撞

为了寻找一种更安全和更先进的密码哈希算法,美国国家标准与技术研究院(National Institute of Standards and Technology,简称 NIST)在 2007 年公开征集 SHA-3 的候选算法。NIST 一共收到了 64 个算法方案,经过多轮的评估和筛选,最终在 2012 年宣布 Keccak 算法胜出,成为 SHA-3 的标准算法(SHA-3 与 SHA-2 算法没有直接的关系)。 Keccak 算法具有与 MD 和 SHA-1/2 完全不同的设计思路,即海绵结构(Sponge Construction),使得传统攻击方法无法直接应用于 SHA-3 的攻击中(能够抵抗目前已知的所有攻击方式包括碰撞攻击、长度扩展攻击、差分攻击等)

由于 SHA-2 算法还没有出现重大的安全漏洞,而且在软件中的效率更高,所以大多数人还是倾向于使用 SHA-2 算法。

相比 MD5 算法,SHA-2 算法之所以更强,主要有两个原因:

  • 哈希值长度更长:例如 SHA-256 算法的哈希值长度为 256 位,MD5 算法的哈希值长度为 128 位
    这就提高了攻击者暴力破解或者彩虹表攻击的难度
  • 更强的碰撞抗性:SHA 算法采用了更复杂的运算过程和更多的轮次,使得攻击者更难以通过预计算或巧合找到碰撞
    目前还没有找到任何两个不同的数据,它们的 SHA-256 哈希值相同

当然,SHA-2 也不是绝对安全的,也有被暴力破解或者彩虹表攻击的风险,所以,在实际的应用中,加盐还是必不可少的

Java 提供了对 SHA 算法系列的支持,包括 SHA-1、SHA-256、SHA-384 和 SHA-512

SHA-256 代码示例(未加盐):

String originalString = "Java学习 + 面试指南:javaguide.cn";
// 创建SHA-256摘要对象
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.update(originalString.getBytes());
// 计算哈希值
byte[] result = messageDigest.digest();
// 将哈希值转换为十六进制字符串
String hexString = new HexBinaryAdapter().marshal(result);
System.out.println("Original String: " + originalString);
System.out.println("SHA-256 Hash: " + hexString.toLowerCase());

输出

Original String: Java学习 + 面试指南:javaguide.cn
SHA-256 Hash: 184eb7e1d7fb002444098c9bde3403c6f6722c93ecfac242c0e35cd9ed3b41cd

慢Hash-Bcrypt

bcrypt 是慢哈希算法,且在很长一段时间内是密码存储的优选方案

但随着硬件技术的发展,其 “仅依赖计算耗时” 的设计逐渐显现短板

而 Argon2 等结合了计算成本和内存成本的算法,在抗现代攻击方面更具优势

不过,对于安全性要求不极致且需兼顾兼容性的场景,bcrypt 仍是可靠选择


Bcrypt算法是一种基于 Blowfish 加密算法的密码哈希算法,专门为密码加密而设计,安全性高

Bcrypt算法可以自动加盐

bcrypt可以从hash后字符串自动提取盐值,这样子用户下一次登陆时候的原始密码就能结合原始盐值进行hash运算得到原来的hash值

由于 Bcrypt 采用了 salt(盐) 和 cost(成本) 两种机制,它可以有效地防止彩虹表攻击和暴力破解攻击,从而保证密码的安全性

salt: 是一个随机生成的字符串,用于和密码混合,增加密码的复杂度和唯一性

cost :是一个数值参数,用于控制 Bcrypt 算法的迭代次数,增加密码哈希的计算时间和资源消耗

Bcrypt 算法可以根据实际情况进行调整加密的复杂度,可以设置不同的 cost 值和 salt 值,从而满足不同的安全需求,灵活性很高

Java 应用程序的安全框架 Spring Security 支持多种密码编码器
其中BCryptPasswordEncoder是官方推荐的一种,它使用 BCrypt 算法对用户的密码进行加密存储

@Bean
public PasswordEncoder passwordEncoder(){
    return new BCryptPasswordEncoder();
}

慢Hash-Argon2

Argon2 是目前密码哈希领域的佼佼者

凭借其抗暴力破解能力和灵活的参数配置,成为许多安全场景的首选

它在 2015 年密码哈希竞赛中夺冠,如今已被广泛应用于各类系统中

核心优势在于能有效抵御GPU 加速攻击 内存 - hard 攻击


为什么需要 Argon2

在密码存储中,直接存储明文密码风险极大,而普通哈希算法(如 MD5、SHA-256)因计算速度快,容易被暴力破解工具(如彩虹表)攻破

Argon2 则通过故意设计的高计算成本和内存消耗,大幅提高破解难度,同时支持并行计算优化,平衡安全性与性能


Argon2 的三种类型及区别

Argon2 有三个变种,适用于不同场景,核心差异在于是否利用数据依赖和内存访问模式:

类型

特点

适用场景

Argon2d

数据依赖的内存访问,抗 GPU 攻击能力最强,但可能存在侧信道攻击风险

对侧信道攻击不敏感的场景(如离线密码哈希)

Argon2i

数据独立的内存访问,通过密码哈希保护内存访问模式,侧信道安全性更高

密码验证(如登录系统)

Argon2id

结合前两者优势,先使用 Argon2i 再用 Argon2d,平衡安全性和抗攻击能力

推荐的通用场景(如大多数应用程序)

  • 普通应用(如用户登录、密码存储):直接用Argon2id,安全性和兼容性最平衡
  • 特殊离线场景(且能确保硬件安全):可选 Argon2d
  • 对侧信道攻击特别敏感的场景(如加密芯片中的密码验证):可选 Argon2i

注意:Argon2id 是目前最推荐的类型,在 RFC 9106 中被指定为标准,兼顾安全性和实用性


应用能力不同的底层原因是啥?

为什么普通应用(登录、密码存储)首选 Argon2id?

普通应用的威胁是 “两头都要防”:

  • 一方面怕黑客用 GPU / 超级计算机暴力破解(比如批量试密码)
  • 另一方面怕有人通过偷偷监控设备(比如测电流、看内存访问规律)来猜密码(侧信道攻击)

而 Argon2id 是 “混合体”:先按 Argon2i 的方式防侧信道攻击,再按 Argon2d 的方式防 GPU 破解,等于 “兼顾了两种防护”。普通应用里,这两种威胁都可能存在,所以选它最稳妥


为什么特殊离线场景可选 Argon2d?

离线场景(比如本地存储的密码文件,不联网验证)的核心威胁是 “被黑客拿到文件后,用 GPU 暴力破解”。
Argon2d 抗 GPU 破解的能力最强,但它的内存访问路径和密码相关,容易被侧信道攻击抓漏洞
但 “离线场景” 通常是在你自己的设备上(比如加密的本地密码管理器),别人很难物理接触设备搞侧信道攻击(总不能撬开你电脑测电流吧),所以可以放心用它的 “强项”


为什么侧信道敏感场景选 Argon2i?

有些场景特别怕 “侧信道攻击”,比如加密芯片(银行 U 盾、智能卡)—— 这些设备体积小,容易被物理接触,黑客可能通过监测它工作时的耗电、发热变化来反推密码
Argon2i 的内存访问路径和密码无关,就算被监测,也泄露不了密码相关的规律,所以侧信道安全性最高。虽然它抗 GPU 破解的能力弱一点,但这类场景里,“防物理监测” 比 “防暴力破解” 更重要


一句话总结:

  • 怕两种攻击都来?选 Argon2id(普通应用基本都这样)。
  • 只怕 GPU 暴力破解,不怕物理监测?选 Argon2d(离线场景)。
  • 最怕物理监测(侧信道攻击)?选 Argon2i(加密芯片等特殊设备)。


 

日常开发不用纠结,直接用 Argon2id,这是官方认证的 “万能款”


核心参数:如何配置 Argon2?

Argon2 的安全性通过以下参数控制,需根据系统性能和安全需求调整:

  1. 时间成本(t):迭代次数(哈希计算的轮数),增加计算时间
  2. 内存成本(m):以 KiB 为单位的内存消耗(如 65536 = 64MB),提高内存需求
  3. 并行度(p):并行计算的线程数,利用多核处理器优化性能,不影响安全性
  4. 盐(salt):随机生成的 16 字节以上字符串,每个密码对应唯一盐值,防止彩虹表攻击
  5. 哈希长度(hash length):输出的哈希值长度(通常 16-32 字节)

示例配置(适用于多数应用):t=3, m=65536, p=4,即 3 轮迭代、64MB 内存、4 线程并行


实际应用:如何使用 Argon2?

大多数编程语言都有 Argon2 的实现库,以下是常见场景的使用示例:

密码加密(Java 示例,使用 argon2-jvm 库)

Argon2 argon2 = Argon2Factory.create(Argon2Factory.Argon2Types.ARGON2id);
String salt = new SecureRandom().generateSeed(16); // 生成16字节盐值
String hash = argon2.hash(3, 65536, 4, salt, "user_password".getBytes());

 密码验证
存储哈希值时需包含盐值和参数(格式:$argon2id$v=19$m=65536,t=3,p=4$salt$hash)
验证时直接使用库函数对比输入密码与存储的哈希值


Hash算法简单总结

什么是Hash算法

哈希算法的是不可逆的,你无法通过哈希之后的值再得到原值

Hash算法分类:

  1. 加密Hash算法:安全性相对较高,但性能较差。适用于数据完整性校验
  2. 非加密Hash算法:安全性相对较低的哈希算法,但性能较好。适用于普通Hash,例如数据分片,Hash槽的Hash表
  3. 特殊Hash算法:安全性更高的慢Hash,增大攻击者的时间成本和设备的内存成本

什么是彩虹表

彩虹表 是一种预先计算好的 哈希值与明文密码的映射表,用于快速破解哈希算法加密的密码

它通过 时间-存储权衡 大幅减少暴力破解所需的时间

尤其对弱密码或未加盐(Unsalted)的哈希攻击效果极强

可以通过加盐和慢Hash增大攻击成本,阻止彩虹表的暴力破解

MD5目前就已经被彩虹表暴力破解了,所以现在不推荐用MD5了,重要数据Hash场景更不该用,用的话要加盐


为什么需要加盐

防止下面两个问题:

  1. 彩虹表攻击:预计算常见密码的哈希值,直接反向查询
  2. 批量破解:同一密码的哈希值相同,破解一个等于破解所有

市面上的加密算法都有彩虹表算出对应的值,不加盐的话容易被猜出来

如果不加盐,破解出了一个密码,攻击者发现 Alice 和 Bob 的哈希相同,直接破解一次即可获取两人密码

  1. 盐值必须是用户唯一的且随机生成的
  2. 盐值长度必须足够长,避免盐值碰撞
  3. 盐值需与密码hash值一起存
  4. 结合慢hash算法,单独加盐仍存在暴力破解的可能,建议配置bcrypt/Argon2/PBKDF2

加密算法SHA256(快+不碰撞)

又快又安全,碰撞概率极低

  1. SHA-256 算法的哈希值长度为 256 位,MD5 算法的哈希值长度为 128 位
    这就提高了攻击者暴力破解或者彩虹表攻击的难度
  2. 目前还没有找到任何两个不同的数据,它们的 SHA-256 哈希值相同

场景:比如我们日常下载软件安装包时,官方通常会提供该安装包的 SHA256 哈希值

这种场景要求性能快和不碰撞,这种hash值破解了也几乎没啥用


非加密Hash(快+不追求低碰撞)

例如普通的模运算

特点:

  1. 计算速度快(比加密哈希快 10-100 倍)
  2. 不保证抗碰撞性(可能被恶意构造碰撞)
  3. 不保证单向性(可能被逆向工程)

场景:

  1. 哈希表:Java 的 HashMap 使用 hashCode()
  2. 分布式系统:分布式系统中,不同节点之间同步数据时,通过计算数据的 SHA256 哈希值,可以快速比对各节点数据是否一致
  3. 缓存键生成:Redis 的键哈希
  4. 数据分片:如数据库分库分表

慢Hash(密码存储)

核心特点:故意降低计算速度,以抵御暴力破解和彩虹表攻击

通常结合加盐多次迭代大幅增加攻击者的破解成本

慢Hash的目的:

让合法用户验证密码时稍微慢一点,但让攻击者破解密码的成本高到无法承受

慢哈希通过 人为引入计算延迟,使得攻击者需要数年甚至更长时间才能破解一个密码

慢Hash算法:

  1. bcrypt
  2. Argon2

底层都有自动加盐,目前推荐Argon2,它在 2015 年密码哈希竞赛中夺冠

bcypt只能从时间层面加大成本,但是Argon2能在时间+内存空间层面加大成本


MD5和SHA-256

推荐使用SHA-256

MD5已经被彩虹表破解

SHA-256适用于数据校验场景

  1. SHA-256 算法的哈希值长度为 256 位,MD5 算法的哈希值长度为 128 位
    这就提高了攻击者暴力破解或者彩虹表攻击的难度
  2. 目前还没有找到任何两个不同的数据,它们的 SHA-256 哈希值相同

慢Hash-Bcrypt和Argon2

bcrypt 是慢哈希算法,且在很长一段时间内是密码存储的优选方案

但随着硬件技术的发展,其 【仅依赖计算耗时】 的设计逐渐显现短板

而 Argon2 等结合了计算成本和内存成本的算法,在抗现代攻击方面更具优势

不过,对于安全性要求不极致且需兼顾兼容性的场景,bcrypt 仍是可靠选择

bcrypt和Argon2底层都自动加盐

Bcrypt

Bcrypt 采用了 salt(盐) 和 cost(成本) 两种机制,它可以有效地防止彩虹表攻击和暴力破解攻击,从而保证密码的安全性

salt: 是一个随机生成的字符串,用于和密码混合,增加密码的复杂度和唯一性

cost :是一个数值参数,用于控制 Bcrypt 算法的迭代次数,增加密码哈希的计算时间和资源消耗


Argon2

Argon2 则通过故意设计的高计算成本和内存消耗,大幅提高破解难度,同时支持并行计算优化,平衡安全性与性能

黑客可以通过暴力运算破解和侧信道攻击(通过物理设备查看电流和内存规律推导出密码)

三种Argon2

  1. Argon2d:防暴力运算,但是侧信道攻击难防。适用于离线场景,离线场景通常是自己的设备,别人很难物理接触设备搞侧信道攻击
  2. Argon2i:防侧信道攻击,但是暴力运算难防。适用于体积小的物理设备,例如芯片,智能卡等物理设备,黑客可能通过监测它工作时的耗电、发热变化来反推密码
  3. Argon2id:两者都兼容

一般场景推荐使用Argon2id

Argon2 的安全性通过以下参数控制,需根据系统性能和安全需求调整:

  1. 时间成本(t):迭代次数(哈希计算的轮数),增加计算时间
  2. 内存成本(m):以 KiB 为单位的内存消耗(如 65536 = 64MB),提高内存需求
  3. 并行度(p):并行计算的线程数,利用多核处理器优化性能,不影响安全性
  4. 盐(salt):随机生成的 16 字节以上字符串,每个密码对应唯一盐值,防止彩虹表攻击
  5. 哈希长度(hash length):输出的哈希值长度(通常 16-32 字节)

示例配置:t=3, m=65536, p=4,即 3 轮迭代、64MB 内存、4 线程并行

你可能感兴趣的:(算法,哈希算法,java,spring,boot,操作系统,spring,密码学)