SQLAlchemy如何对加密列执行LIKE模糊查询?
加密列模糊查询的问题解决方法
问题原因
你用EncryptedType把字段加密成BLOB存储后,数据库里存的是加密后的二进制数据。当你执行like查询时,SQLAlchemy会直接把%Vholran%作为条件发给数据库,数据库是对加密后的BLOB做模糊匹配,而不是明文,自然找不到结果。而精确匹配能生效,是因为SQLAlchemy会先把你传入的明文'Vholran'加密,再和数据库里的加密值做对比,匹配逻辑是对的。
可行解决方案
1. 客户端过滤(简单直接,小数据量适用)
先把所有用户数据查出来,在Python代码里对明文做模糊匹配:
name_to_look = 'Vholran' # 先查询所有用户对象 all_users = db.session.query(User).all() # 在Python端过滤包含目标字符串的记录 matched_users = [user for user in all_users if name_to_look in user.fullname]
这种方法不用改数据库或加密逻辑,但如果用户表数据量很大(比如上万条以上),性能会很差,因为要全表查询并加载所有数据到内存。
2. 改用可搜索的加密方案(大数据量推荐)
如果数据量较大,需要在数据库端实现模糊匹配,得换用支持搜索的加密方式,常见的思路有两种:
(1)盲索引(Blind Index)
针对模糊查询的需求,比如要匹配包含Vholran的记录,可以提前生成fullname所有可能子串的哈希值(加盐后),存到一个单独的字段里。查询时,生成目标字符串的所有子串哈希,再匹配这个索引字段。
示例实现思路:
- 给User模型加一个
fullname_blind_indices字段,类型为db.ARRAY(db.String)或者用单独的关联表存储索引 - 当保存User时,生成
fullname的所有子串(比如长度3以上的子串),每个子串加盐后哈希,存入索引字段 - 查询时,生成
Vholran的所有子串哈希,然后用fullname_blind_indices.any(哈希值)来匹配
注意:要给哈希加盐,避免彩虹表攻击;同时要接受一定的误匹配概率(哈希碰撞),或者用双哈希降低风险。
(2)确定性加密(Deterministic Encryption)
如果只需要前缀模糊匹配(比如Vhol%),可以用确定性加密算法(相同明文加密后得到相同密文),然后把明文的前缀加密后,用like匹配加密后的前缀。但这种加密方式安全性比随机加密低,容易被频率分析攻击,需要权衡安全性和需求。
3. 避免直接在加密BLOB上做数据库端模糊匹配
不要尝试直接对加密后的BLOB字段用like,因为AES加密后的二进制数据是无规律的,子串匹配完全没有意义,只会浪费性能。
内容的提问来源于stack exchange,提问作者Seven
相关产品推荐
相关产品推荐

