如何在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:应用层全量过滤(适合小数据集)
直接从数据库取出所有加密记录,在应用层逐一解密后和搜索词对比。这种方式简单但性能极差,数据量大时完全不可用。
额外注意点
- 密钥持久化:原代码每次运行都会生成新的Fernet密钥,之前加密的数据会永久无法解密,必须把密钥保存到安全的地方。
- SQL注入风险:原代码用字符串格式化拼接SQL,存在严重注入漏洞,必须改用参数化查询。
内容的提问来源于stack exchange,提问作者user18939954
相关产品推荐
相关产品推荐

