如何通过AES_ENCRYPT和AES_DECRYPT函数在MySQL中实现高效加解密操作?

2026-05-07 19:051阅读0评论SEO资源
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计906个文字,预计阅读时间需要4分钟。

如何通过AES_ENCRYPT和AES_DECRYPT函数在MySQL中实现高效加解密操作?

MySQL 自带的 `AES_ENCRYPT` 和 `AES_DECRYPT` 可以进行加密和解密,但它们不能用于安全存储——它们不自动加盐、不进行迭代、不管理密钥,全依赖你自己。不留意的话,安全风险很大。

为什么 AES_ENCRYPT 解出来的值总是 NULL

最常见原因是密钥长度不对或数据类型不匹配。MySQL 的 AES 实现严格遵循 NIST 标准,只接受 128/192/256 位密钥(即 16/24/32 字节的字符串),传入 16 个数字字符 '1234567890123456' 可以,但传 '12345678'(8 字节)就会静默失败,返回 NULL

实操建议:

  • UNHEX()SHA2() 生成固定长度密钥,比如 SHA2('my_secret_key', 256) 得到 64 字符 hex 字符串,再用 UNHEX(LEFT(SHA2('my_secret_key', 256), 64)) 截取前 32 字节作为 256 位密钥
  • 加密字段必须是 BLOBVARBINARY 或能容纳二进制数据的类型;如果存进 VARCHAR 且没设 COLLATE utf8mb4_bin,可能被截断或乱码
  • 确认 MySQL 版本 ≥ 5.7.6 —— 旧版默认用 ECB 模式,无 IV,极不安全;5.7.6+ 默认用 ECB,但可通过 AES_ENCRYPT(str, key, iv) 显式指定 CBC 模式(需额外传第三个参数)

AES_DECRYPT 返回空字符串而不是原始内容

不是解密失败,而是解密后结果被 MySQL 当作非 UTF-8 字符串处理,转成字符串时丢弃了不可见字节(比如开头的 \0)。典型表现:加密后查出来是乱码,AES_DECRYPT 结果长度为 0,但用 LENGTH(AES_DECRYPT(...)) 发现其实有字节数。

实操建议:

  • 解密后立刻用 CONVERT(... USING utf8mb4)CAST(... AS CHAR) 强制转成可读字符串
  • 更稳妥的做法是:加密前先用 TO_BASE64() 编码明文,再加密;解密后再 FROM_BASE64() —— 这样全程都是 ASCII 安全字符,避免二进制污染
  • 别在应用层直接拼接 SQL 解密语句,尤其密钥来自用户输入——AES_DECRYPT 不防注入,恶意构造的密钥可能导致服务端报错泄露信息

AES_ENCRYPT 存密码?千万别

它不加盐、不慢哈希、不抗暴力穷举。哪怕你用了 32 字节密钥,攻击者拿到密文 + 知道你用的是 AES-CBC,就能用 GPU 批量跑密钥。真实业务中,密码必须用 PASSWORD()(已弃用)、SHA2() 加盐,或者更好——交给应用层用 bcrypt / argon2 处理。

它的合理用途其实是:临时保护敏感字段(如手机号、邮箱)在数据库内“不可直读”,且密钥由应用统一管理、不落地到 SQL 日志中。例如:

INSERT INTO users (name, phone_enc) VALUES ('张三', AES_ENCRYPT(TO_BASE64('13800138000'), @key));

查询时:

SELECT name, FROM_BASE64(AES_DECRYPT(phone_enc, @key)) AS phone FROM users;

IV(初始向量)若复用、或固定写死,会极大削弱 CBC 安全性;每次加密必须生成新随机 IV,并和密文一起存(比如拼在密文前面,解密时拆开),MySQL 本身不帮你管这个。

标签:Mysql

本文共计906个文字,预计阅读时间需要4分钟。

如何通过AES_ENCRYPT和AES_DECRYPT函数在MySQL中实现高效加解密操作?

MySQL 自带的 `AES_ENCRYPT` 和 `AES_DECRYPT` 可以进行加密和解密,但它们不能用于安全存储——它们不自动加盐、不进行迭代、不管理密钥,全依赖你自己。不留意的话,安全风险很大。

为什么 AES_ENCRYPT 解出来的值总是 NULL

最常见原因是密钥长度不对或数据类型不匹配。MySQL 的 AES 实现严格遵循 NIST 标准,只接受 128/192/256 位密钥(即 16/24/32 字节的字符串),传入 16 个数字字符 '1234567890123456' 可以,但传 '12345678'(8 字节)就会静默失败,返回 NULL

实操建议:

  • UNHEX()SHA2() 生成固定长度密钥,比如 SHA2('my_secret_key', 256) 得到 64 字符 hex 字符串,再用 UNHEX(LEFT(SHA2('my_secret_key', 256), 64)) 截取前 32 字节作为 256 位密钥
  • 加密字段必须是 BLOBVARBINARY 或能容纳二进制数据的类型;如果存进 VARCHAR 且没设 COLLATE utf8mb4_bin,可能被截断或乱码
  • 确认 MySQL 版本 ≥ 5.7.6 —— 旧版默认用 ECB 模式,无 IV,极不安全;5.7.6+ 默认用 ECB,但可通过 AES_ENCRYPT(str, key, iv) 显式指定 CBC 模式(需额外传第三个参数)

AES_DECRYPT 返回空字符串而不是原始内容

不是解密失败,而是解密后结果被 MySQL 当作非 UTF-8 字符串处理,转成字符串时丢弃了不可见字节(比如开头的 \0)。典型表现:加密后查出来是乱码,AES_DECRYPT 结果长度为 0,但用 LENGTH(AES_DECRYPT(...)) 发现其实有字节数。

实操建议:

  • 解密后立刻用 CONVERT(... USING utf8mb4)CAST(... AS CHAR) 强制转成可读字符串
  • 更稳妥的做法是:加密前先用 TO_BASE64() 编码明文,再加密;解密后再 FROM_BASE64() —— 这样全程都是 ASCII 安全字符,避免二进制污染
  • 别在应用层直接拼接 SQL 解密语句,尤其密钥来自用户输入——AES_DECRYPT 不防注入,恶意构造的密钥可能导致服务端报错泄露信息

AES_ENCRYPT 存密码?千万别

它不加盐、不慢哈希、不抗暴力穷举。哪怕你用了 32 字节密钥,攻击者拿到密文 + 知道你用的是 AES-CBC,就能用 GPU 批量跑密钥。真实业务中,密码必须用 PASSWORD()(已弃用)、SHA2() 加盐,或者更好——交给应用层用 bcrypt / argon2 处理。

它的合理用途其实是:临时保护敏感字段(如手机号、邮箱)在数据库内“不可直读”,且密钥由应用统一管理、不落地到 SQL 日志中。例如:

INSERT INTO users (name, phone_enc) VALUES ('张三', AES_ENCRYPT(TO_BASE64('13800138000'), @key));

查询时:

SELECT name, FROM_BASE64(AES_DECRYPT(phone_enc, @key)) AS phone FROM users;

IV(初始向量)若复用、或固定写死,会极大削弱 CBC 安全性;每次加密必须生成新随机 IV,并和密文一起存(比如拼在密文前面,解密时拆开),MySQL 本身不帮你管这个。

标签:Mysql