aes_encrypt与aes_decrypt必须严格配对使用,密钥、iv和加密模式三者完全一致,否则静默返回null;字段类型须为varbinary/blob或hex编码存varchar,iv须显式生成存储且不可复用,密钥长度须匹配16/24/32字节并避免硬编码。

AES_ENCRYPT 和 AES_DECRYPT 必须配对使用,且密钥、IV、加密模式三者必须完全一致,否则解密结果为 NULL —— 这是实际写 SQL 时最常踩的坑。
加密字段类型必须是 BLOB / VARBINARY,不能用 VARCHAR
MySQL 的 AES_ENCRYPT 返回的是二进制数据(BLOB),如果强行存进 VARCHAR 字段,会因字符集转换导致数据损坏,查询时 AES_DECRYPT 返回 NULL。
- 正确做法:字段类型声明为
VARBINARY(256)或BLOB(长度按加密后最大值预估,AES-256 加密后长度 ≈ 原文长度 + 16 字节对齐) - 错误示例:
encrypted_phone VARCHAR(100)→ 插入成功但解密失败 - 备选方案:用
HEX(AES_ENCRYPT(...))存字符串,读取时先UNHEX()再解密,但多一次函数调用,且字段需设为VARCHAR(512)(HEX 后长度翻倍)
必须显式管理 IV(初始化向量),不能省略或复用
MySQL 默认使用 ECB 模式(不安全),而生产环境必须用 CBC 或 GCM 模式;AES_ENCRYPT(str, key) 实际等价于 AES_ENCRYPT(str, key, RANDOM_BYTES(16)),但隐式 IV 会导致每次加密结果不可重现,且无法安全解密。
- 务必显式生成并存储 IV:用
RANDOM_BYTES(16)生成,存入单独的iv VARBINARY(16)字段 - 解密时必须传入原始 IV:
AES_DECRYPT(encrypted_data, key, iv) - 绝对不要复用 IV:同一密钥下重复使用 IV 会严重削弱安全性,尤其对相同明文会产出相同密文
密钥不能硬编码在 SQL 里,也不能用短字符串
把密钥写死在 INSERT 或 SELECT 语句中,等于把保险柜密码贴在柜子上。MySQL 本身不提供密钥轮换或权限隔离机制,密钥泄露即全盘失守。
- 密钥长度必须匹配算法:AES-128 要 16 字节,AES-192 要 24 字节,AES-256 要 32 字节;用
SHA2('your-key-string', 256)截取前 32 字节更稳妥 - 应用层传参:通过预处理语句(如
INSERT INTO ... VALUES (?, ?))由应用传入密钥和 IV,避免密钥出现在查询日志或慢日志中 - 禁止用
MD5()或PASSWORD()做加密密钥——它们是哈希函数,不是密钥派生函数(KDF),缺乏抗暴力破解能力
AES_DECRYPT 失败时静默返回 NULL,没有错误提示
这是 MySQL 加密函数最危险的设计:密钥错、IV 错、字段类型错、甚至数据被截断,统统返回 NULL,而不是报错。你可能以为“解密成功”,其实只是没数据。
- 验证方式:在插入后立即用同参数 SELECT 解密,确认结果与原文一致
- 调试技巧:用
LENGTH(encrypted_data)检查加密后长度是否合理(比如原文 11 位手机号,AES-256-CBC 加密后应为 32 字节) - 上线前必做:用已知明文 + 固定 IV + 固定密钥写测试用例,确保加解密可逆
真正难的不是调用那两个函数,而是让 IV 可追溯、密钥不落地、字段类型不踩坑、解密失败能感知——这些细节藏在业务 SQL 的间隙里,却决定着加密是否真的有效。











