正确用法是:必须显式指定CBC等安全模式、16字节随机IV(如RANDOM_BYTES(16))及固定长度二进制密钥(如UNHEX(SHA2('key',256))),字段用VARBINARY存储,IV单独存列,严禁硬编码密钥或复用IV。

MySQL中AES_ENCRYPT加密手机号的正确用法
AES_ENCRYPT不是“开箱即用”的安全方案,直接套用会出问题。它默认使用 ECB 模式(AES_ENCRYPT(str, key)),不带 IV、无盐、不防重放,相同手机号永远生成相同密文,极易被字典/彩虹表攻击——这在手机号这种低熵字段上尤其危险。
必须显式指定模式和 IV 才能勉强达到可用级别:
-
AES_ENCRYPT(phone, UNHEX(SHA2('your_secret_key', 256)))仅做密钥派生,仍不够 - 务必配合
AES_DECRYPT+IV参数:用ENCRYPT的第二个参数传入随机 IV(如RANDOM_BYTES(16)),再把 IV 和密文拼接存储 - 推荐用
AES_ENCRYPT(phone, @key, @iv)形式(MySQL 5.7.4+ 支持三参数),避免手动拼接出错
存储加密手机号时必须处理的字段结构
不能只建一个 phone_encrypted VARCHAR 字段存密文——密文是二进制数据,直接转字符串可能截断或乱码。
正确做法是:
- 用
VARBINARY(255)类型存密文(ECB 下约 16×⌈n/16⌉ 字节;CBC/GCM 更长) - 单独加一列
phone_iv类型也设为VARBINARY(16),存随机 IV - 如果用三参数
AES_ENCRYPT(phone, @key, @iv),IV 必须是 12 或 16 字节(取决于模式),否则报错Invalid key or IV length - 别把密钥硬编码进 SQL;用应用层传参,或 MySQL 变量
SET @key = UNHEX(SHA2('app_key', 256))
查询加密手机号时的性能与语法陷阱
WHERE 条件里不能对密文字段做函数运算(如 AES_DECRYPT(phone_encrypted, @key) = '138****1234'),会导致全表扫描,索引完全失效。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
可行方案只有两种:
- 应用层先加密查询值,再查密文:
SELECT * FROM users WHERE phone_encrypted = AES_ENCRYPT(?, @key, @iv)—— 但需确保 IV 一致(通常查操作不涉及 IV 生成,只用于解密显示) - 若必须模糊匹配(如查号段),只能解密后在应用层过滤,无法走数据库索引
- 注意
AES_DECRYPT返回NULL而非空字符串,当密钥错误或数据损坏时,IS NOT NULL判断比!= ''更可靠
MySQL版本与加密模式兼容性要点
MySQL 5.7 默认只支持 ECB 和 CBC;8.0.13+ 才支持 GCM 模式(带认证),但 AES_ENCRYPT 函数本身不暴露 mode 参数,实际仍受限于底层 OpenSSL 版本。
关键限制:
- ECB 模式(两参数调用)绝对禁用——
AES_ENCRYPT('13812345678', 'key')输出可预测 - CBC 模式需严格 16 字节 IV,且必须每次生成新 IV(
RANDOM_BYTES(16)),不能复用 - MySQL 不提供密钥派生函数(如 PBKDF2),
SHA2做密钥拉伸太弱,建议应用层用scrypt或argon2处理后再传入 - 备份与主从同步时,密文字段正常复制,但密钥管理必须独立部署,切勿写死在配置文件里
真正麻烦的从来不是怎么调用 AES_ENCRYPT,而是 IV 怎么安全分发、密钥怎么轮换、解密失败时怎么审计、以及——你是否真的需要数据库层加密,而不是让应用或中间件来承担这件事。

















