防止“彩虹表攻击”:计算 hash 前加 salt, 完整的流程是怎样的,这个 salt 是如何存放的
为了防止“彩虹表攻击”,在计算哈希值之前添加一个随机的盐(salt)是常见的做法。盐是一种随机数据,它与密码结合后再进行哈希运算,使得相同的密码每次生成的哈希值都不同,从而有效防止彩虹表攻击。下面是完整的流程以及盐的存放方式。
完整的流程
-
生成随机盐:
- 为每个用户生成一个唯一的随机盐。盐的长度通常为 16 字节或更长。
-
将盐与密码结合:
- 将生成的盐与用户的密码结合在一起。通常是将盐附加在密码的前面或后面。
-
计算哈希值:
- 使用密码学哈希函数(例如 SHA-256、SHA-512、bcrypt、scrypt 等)对盐和密码的组合进行哈希运算,得到哈希值。
-
存储盐和哈希值:
- 将生成的盐和哈希值一起存储在数据库中。通常将盐和哈希值存储在同一个字段中,或者分别存储在两个字段中。
具体实现示例
以下是一个伪代码示例,展示如何生成盐、计算哈希值以及存储盐和哈希值:
import os
import hashlib
import binascii
def generate_salt(length=16):
"""生成指定长度的随机盐"""
return os.urandom(length)
def hash_password(password, salt):
"""计算密码的哈希值"""
# 将盐与密码结合
salted_password = salt + password.encode('utf-8')
# 计算哈希值
hash_value = hashlib.sha256(salted_password).digest()
return binascii.hexlify(hash_value).decode('utf-8')
def store_password(username, password):
"""存储用户名、盐和哈希值"""
# 生成随机盐
salt = generate_salt()
# 计算哈希值
hashed_password = hash_password(password, salt)
# 将盐和哈希值存储在数据库中
# 假设使用一个简单的字典来模拟数据库
database[username] = {
'salt': binascii.hexlify(salt).decode('utf-8'),
'hashed_password': hashed_password
}
def verify_password(username, password):
"""验证用户输入的密码"""
# 从数据库中获取 存储的盐和哈希值
salt = binascii.unhexlify(database[username]['salt'])
stored_hashed_password = database[username]['hashed_password']
# 计算输入密码的哈希值
hashed_password = hash_password(password, salt)
# 比较哈希值
return hashed_password == stored_hashed_password
# 模拟数据库
database = {}
# 存储用户密码
store_password('user1', 'my_secure_password')
# 验证用户密码
is_valid = verify_password('user1', 'my_secure_password')
print(f"Password is valid: {is_valid}")
盐的存放方式
- 与哈希值一起存储:最常见的方法是将盐和哈希值一起存储在数据库的同一个字段中,使用分隔符将它们分开。例如,存储格式可以是
salt:hashed_password。 - 分别存储:也可以将盐和哈希值分别存储在数据库的两个独立字段中。例如,数据库表可以有
salt和hashed_password两个字段。
存储示例
假设我们使用一个 SQL 数据库来存储用户信息,表结构可能如下:
CREATE TABLE users (
username VARCHAR(255) PRIMARY KEY,
salt VARCHAR(255) NOT NULL,
hashed_password VARCHAR(255) NOT NULL
);
在存储用户信息时:
INSERT INTO users (username, salt, hashed_password)
VALUES ('user1', 'generated_salt', 'hashed_password');
在验证用户密码时:
SELECT salt, hashed_password FROM users WHERE username = 'user1';
总结
通过在计算哈希值之前添加随机盐,可以有效防止彩虹表攻击。完整的流程包括生成随机盐、将盐与密码结合、计算哈希值,以及将盐和哈希值一起存储在数据库中。这样,即使攻击者获取了哈希值,也难以通过彩虹表进行逆向破解。
数据库被盗,salt 和 hashed_password 岂不是同时丢失了
确实,如果数据库被盗,攻击者可能会同时获得盐(salt)和哈希值(hashed_password)。尽管如此,使用盐仍然是防止彩虹表攻击的有效方法,因为每个密码都有不同的盐,这使得攻击者无法使用预计算的彩虹表来破解哈希值。
然而,为了进一步提升安全性,可以采取以下措施:
1. 使用强哈希算法
选 择强大的哈希算法,如 bcrypt、scrypt 或 Argon2。这些算法专为密码哈希设计,具有以下特点:
- 慢速计算:增加破解时间。
- 内存消耗:增加破解难度。
2. 增加盐的长度
使用更长的盐(例如 32 字节或更长),增加攻击者生成对应彩虹表的难度。
3. 使用多重加密
在盐和密码组合后,使用多次哈希(例如,迭代 1000 次哈希),增加破解难度。
4. 分离存储
将盐和哈希值分开存储,以减少攻击者同时获取两者的可能性。例如,将盐存储在一个单独的安全位置(如不同的数据库或文件系统)。
5. 使用密钥推导函数(KDF)
使用密钥推导函数(如 PBKDF2、bcrypt、scrypt 或 Argon2)来生成哈希值,这些函数设计用于安全地处理密码。