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

如何在cryptography.fernet加密的数据库字段中实现搜索?

解决Fernet加密后数据库无法匹配搜索的问题

你遇到的问题根源确实是Fernet的设计特性:它每次加密同一明文时会使用随机生成的初始化向量(IV),所以生成的密文完全不同,直接用搜索词加密后的结果去匹配数据库里的密文自然找不到。下面是几个可行的解决思路:

方案1:存储明文的加盐哈希值(推荐)

在数据库里额外添加字段存储明文的加盐哈希值,搜索时计算搜索词的加盐哈希值,用哈希值匹配数据库中的对应字段,匹配成功后再取出密文解密。这种方式既保留了Fernet加密的安全性,又能实现高效搜索。

修改后的代码示例

import mysql.connector
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
import os

# 密钥必须持久化保存,不能每次运行生成新的!示例存到本地文件,实际可存在环境变量或专业密钥服务
KEY_FILE = "fernet_key.key"
if os.path.exists(KEY_FILE):
    with open(KEY_FILE, "rb") as f:
        key = f.read()
else:
    key = Fernet.generate_key()
    with open(KEY_FILE, "wb") as f:
        f.write(key)
f = Fernet(key)

# 生成加盐哈希,盐随机生成并和哈希值一起存储
def generate_hash(plaintext):
    salt = os.urandom(16)
    kdf = PBKDF2HMAC(
        algorithm=hashes.SHA256(),
        length=32,
        salt=salt,
        iterations=480000,
    )
    hash_val = kdf.derive(plaintext)
    return salt.hex(), hash_val.hex()

db = mysql.connector.connect(
    host="x",
    user="xx",
    password="xxx",
    database="example"
)
mycursor = db.cursor(buffered=True)

# 先确保表有哈希相关字段:ALTER TABLE e1 ADD COLUMN name_salt VARCHAR(32), ADD COLUMN name_hash VARCHAR(64);
userchoice = input("what you want \n1:insert in db \n2:get from db")
if userchoice == '1':
    text = input("name: ").lower().encode()
    text2 = input("cast: ").lower().encode()
    # 生成加盐哈希
    name_salt, name_hash = generate_hash(text)
    # 使用参数化查询避免SQL注入
    sql = "INSERT INTO e1 (name, cast, name_salt, name_hash) VALUES (%s, %s, %s, %s)"
    val = (f.encrypt(text).decode(), f.encrypt(text2).decode(), name_salt, name_hash)
    mycursor.execute(sql, val)
    db.commit()
elif userchoice == '2':
    text = input("name: ").lower().encode()
    # 查询所有记录后在应用层验证哈希
    sql = "SELECT name, cast, name_salt, name_hash FROM e1"
    mycursor.execute(sql)
    results = mycursor.fetchall()
    for row in results:
        stored_salt = bytes.fromhex(row[2])
        stored_hash = bytes.fromhex(row[3])
        # 验证哈希是否匹配
        kdf = PBKDF2HMAC(
            algorithm=hashes.SHA256(),
            length=32,
            salt=stored_salt,
            iterations=480000,
        )
        try:
            kdf.verify(text, stored_hash)
            # 哈希匹配,解密输出内容
            print(f"Name: {f.decrypt(row[0].encode()).decode()}")
            print(f"Cast: {f.decrypt(row[1].encode()).decode()}")
        except:
            continue

db.close()

方案2:使用确定性加密(谨慎使用)

如果必须直接通过密文匹配搜索,可以改用确定性加密算法(相同明文生成相同密文),但这种方式会降低安全性——攻击者可通过密文的重复模式分析明文信息。比如使用AES-CBC模式但固定IV(不推荐),或专门的确定性加密方案。

注意:Fernet本身不支持确定性加密,需要更换加密方式。

方案3:应用层全量过滤(适合小数据集)

直接从数据库取出所有加密记录,在应用层逐一解密后和搜索词对比。这种方式简单但性能极差,数据量大时完全不可用。

额外注意点

  1. 密钥持久化:原代码每次运行都会生成新的Fernet密钥,之前加密的数据会永久无法解密,必须把密钥保存到安全的地方。
  2. SQL注入风险:原代码用字符串格式化拼接SQL,存在严重注入漏洞,必须改用参数化查询。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 00:02:23