You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何相同盐值与加密流程下密码加密输出不一致?

问题:相同盐值与加密流程下输出结果不一致的原因及修复

问题场景

使用相同盐值对同一密码执行加密操作,理论上输出应一致,但实际结果不同,代码如下:

import os
from cryptography.fernet import Fernet
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
import base64


def encrypt_password(password, salt=None):
    # Create a password hash
    if not salt:
        salt = os.urandom(64)
        print("...")
        token = base64.b64encode(salt).decode('utf-8')

    password = password.encode()
    kdf = PBKDF2HMAC(
        algorithm=hashes.SHA256,
        iterations=100000,
        length=32,
        salt=salt,
        backend=default_backend()
    )
    key = base64.urlsafe_b64encode(kdf.derive(password))
    # Encrypt the password
    f = Fernet(key)
    encrypted_password = f.encrypt(password)
    return encrypted_password, salt

x, salt2 = encrypt_password("Hello")
print(x)

l, salt1 = encrypt_password("Hello", salt2)
print(l)

print(salt1 == salt2)

已确认盐值未发生变化,怀疑是库的不一致性问题。

原因分析

  1. Fernet加密的安全特性:Fernet每次执行encrypt()时,会自动生成随机初始化向量(IV)并嵌入加密结果中。即使密钥、明文完全相同,不同的IV会导致加密输出不同,但解密时可通过结果携带的IV还原明文。这是刻意设计的安全机制,避免相同明文加密后出现重复密文,防止被暴力破解。
  2. 登录逻辑的错误设计:当前代码用加密而非哈希存储密码,不符合登录系统安全规范——加密的密码可被解密还原,而密码应使用不可逆的哈希算法处理,避免泄露后被还原。

修复方案

方案1:正确实现登录密码的哈希验证(推荐)

登录系统应存储密码的哈希值,验证时用相同盐值重新计算输入密码的哈希,再与存储值对比:

import os
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
import base64

def hash_password(password, salt=None):
    password = password.encode()
    if not salt:
        salt = os.urandom(64)
    kdf = PBKDF2HMAC(
        algorithm=hashes.SHA256,
        iterations=100000,
        length=32,
        salt=salt,
        backend=default_backend()
    )
    # 直接派生不可逆的哈希值
    password_hash = base64.b64encode(kdf.derive(password))
    return password_hash, salt

# 注册/存储密码时
stored_hash, stored_salt = hash_password("Hello")

# 登录验证时
input_hash, _ = hash_password("Hello", stored_salt)
print(input_hash == stored_hash)  # 输出True,验证通过

方案2:强制Fernet使用固定IV(不推荐,存在安全风险)

如果仅为测试需要让加密结果相同,可手动构造Fernet底层加密器并指定固定IV,但这会破坏Fernet的安全性(相同明文生成相同密文,易被破解):

import os
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
import base64

def encrypt_with_fixed_iv(password, salt=None, fixed_iv=b'\x00'*16):
    password = password.encode()
    if not salt:
        salt = os.urandom(64)
    kdf = PBKDF2HMAC(
        algorithm=hashes.SHA256,
        iterations=100000,
        length=32,
        salt=salt,
        backend=default_backend()
    )
    key = kdf.derive(password)
    # 用固定IV构造AES-CBC加密器(Fernet底层实现)
    cipher = Cipher(algorithms.AES(key), modes.CBC(fixed_iv), backend=default_backend())
    encryptor = cipher.encryptor()
    # 填充明文至AES块大小的倍数
    pad_length = 16 - (len(password) % 16)
    padded_password = password + bytes([pad_length])*pad_length
    ciphertext = encryptor.update(padded_password) + encryptor.finalize()
    # 按Fernet格式打包结果
    encrypted = base64.urlsafe_b64encode(b'\x80' + fixed_iv + ciphertext)
    return encrypted, salt

x, salt2 = encrypt_with_fixed_iv("Hello")
print(x)

l, salt1 = encrypt_with_fixed_iv("Hello", salt2)
print(l)  # 输出与x完全相同
print(salt1 == salt2)  # 输出True

内容的提问来源于stack exchange,提问作者Jordy Dongen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 22:05:19