SQL Server中ENCRYPTBYKEY需显式OPEN SYMMETRIC KEY且密钥不可硬编码;MySQL AES_ENCRYPT要注意字符集、填充及密钥长度;PostgreSQL pgcrypto需处理权限与会话变量;加密字段无法索引,须用哈希或确定性加密折中。

SQL Server 里用 ENCRYPTBYKEY 实现插入时自动加密
直接上结论:SQL Server 存储过程中能用 ENCRYPTBYKEY 加密字段,但必须先打开对称密钥,且密钥生命周期要自己管。不手动 OPEN SYMMETRIC KEY,执行就报错 Cannot find the symmetric key。
常见错误是把加密逻辑写进存储过程后,测试时一切正常,上线后突然全量失败——因为生产环境没执行过 OPEN SYMMETRIC KEY,或者会话一结束密钥就自动关闭了。
- 必须在存储过程开头显式调用
OPEN SYMMETRIC KEY ... WITH PASSWORD = 'xxx' - 密钥密码不能硬编码在存储过程里(会被
sp_helptext看到),建议用 Windows 身份或证书保护密钥 - 加密结果是
varbinary(8000),目标列类型必须匹配,别用varchar存,否则解密时字节错乱 - 同一密钥下,相同明文每次加密结果不同(加了随机 salt),所以不能拿加密值做等值查询,得靠解密后比对或建计算列+索引
MySQL 8.0+ 用 AES_ENCRYPT 的坑:字符集和填充问题
MySQL 没有内置密钥管理,AES_ENCRYPT 必须自己传密钥,而且对输入长度敏感。最常踩的坑是:插入中文时报错 Data too long for column,或解密后末尾多出乱码。
根本原因是 AES_ENCRYPT 默认用 PKCS#5 填充,原始数据长度不是 16 的倍数时会补字节;如果字段定义为 VARBINARY(255),但加密后实际占 272 字节,就截断了。
- 加密前用
CONVERT(@plaintext USING utf8mb4)统一字符集,避免隐式转换导致长度误判 - 目标列至少设为
VARBINARY(255 + 16)(预留最大填充空间) - 密钥必须恰好 16/24/32 字节,用
SHA2('mykey', 256)截取可保长度,别直接用短字符串 -
AES_DECRYPT返回VARBINARY,要转回字符串得显式CONVERT(... USING utf8mb4)
PostgreSQL 中触发器 + pgcrypto 自动加密的权限陷阱
PostgreSQL 推荐用触发器实现“写入即加密”,但 pgcrypto 函数默认只有 superuser 能用。普通用户执行存储过程时调用 encrypt,会直接报错 permission denied for function encrypt。
这不是语法问题,是权限链断裂:触发器以表 owner 身份运行,但如果 owner 是 postgres,而应用连的是普通用户,函数权限不会自动继承。
- 给函数加
SECURITY DEFINER并用ALTER FUNCTION encrypt(...) SECURITY DEFINER重设 - 函数 owner 必须是可信角色(比如专用加密角色),且该角色不能被普通用户
SET ROLE切换 - 密钥别存在函数体里,用
current_setting('app.crypto_key', true)从会话变量读,由应用层设置 - 触发器中别用
NEW.column := encrypt(...)直接赋值,先IF NEW.column IS NOT NULL判空,否则 NULL 加密后变空字符串
所有数据库共通的硬伤:加密字段没法走索引优化查询
只要字段加密了,WHERE encrypted_email = encrypt('a@b.com') 这种写法永远无法用索引——因为每次加密结果不同,数据库没法预建 B-Tree 分支。
想查得快,只能妥协:要么额外存一个哈希(如 HMAC-SHA256)用于等值查找,要么用确定性加密(如 AES-SIV),但后者要求数据库原生支持,目前仅 SQL Server 2016+ 的 Always Encrypted 和 PostgreSQL 15+ 的 pg_tde 插件能稳住。
- 哈希方案记得加盐,否则彩虹表一扫就崩
- 确定性加密禁用随机 IV,安全强度下降,别用于高敏字段如身份证号
- 应用层做模糊搜索(LIKE)或范围查询(BETWEEN)时,必须先解密全量数据再过滤,性能随数据量陡增
加密不是开关一按就完事,密钥轮换、日志脱敏、备份文件是否含密文、DBA 能否导出原始值……这些环节漏掉一个,前面全白干。










