mysql中的加密函数与数据保护

来源:这里教程网 时间:2026-02-28 20:52:46 作者:

MySQL 里没有真正意义上的「加密函数」,只有单向哈希函数

很多人在

SELECT
里用
ENCRYPT()
MD5()
SHA1()
时,以为是在「加密」,其实这些全是不可逆的哈希——你无法从结果反推出原始密码。MySQL 原生不提供 AES 加密/解密以外的双向加解密能力(且
AES_ENCRYPT()
要求密钥长度严格匹配,否则返回
NULL
)。

常见误用场景:

MD5('password')
存用户密码 → 一旦哈希被撞库或彩虹表破解,等同于明文泄露
试图用
ENCRYPT()
(仅 Linux crypt() 支持)做跨平台存储 → Windows 下直接失效
调用
AES_DECRYPT()
但没传对
iv
或密钥编码格式(比如用 UTF-8 字符串当二进制密钥)→ 返回
NULL
不报错,极难排查

AES_ENCRYPT / AES_DECRYPT 的正确用法和陷阱

这是 MySQL 中唯一可用的对称加解密函数族,但极易出错。关键不是“能不能用”,而是“怎么用才不会丢数据”。

AES_ENCRYPT()
AES_DECRYPT()
必须使用相同模式(默认
ecb
,但推荐显式指定
cbc
)、相同密钥、相同初始化向量(
iv
);
iv
长度必须为 16 字节
密钥不能直接传字符串:用
UNHEX('30313233343536373839616263646566')
CAST('my16byteskey...' AS BINARY)
确保是二进制
加密结果是
VARBINARY
,存入字段前务必确认列类型足够长(
AES_ENCRYPT()
输出长度 ≥ 原文长度 + 16)
MySQL 5.7+ 默认关闭
block_encryption_mode
系统变量,需手动设为
aes-128-cbc
才支持
iv
AES_ENCRYPT('hello', UNHEX('000102030405060708090a0b0c0d0e0f'), UNHEX('101112131415161718191a1b1c1d1e1f'))

密码存储该用什么?别自己造轮子

MySQL 本身不提供 bcrypt/scrypt/argon2 支持,所以「在数据库层做安全密码哈希」这个需求,答案很明确:不该由 MySQL 做。

应用层用
bcrypt.hashpw()
(Python)、
crypto.pbkdf2()
(Node.js)或
password_hash()
(PHP)生成哈希,再存入 MySQL 的
VARCHAR(255)
字段
如果非要在 SQL 层处理(如 ETL 场景),至少用
SHA2('pass', 512)
+ 盐值拼接,并确保盐值随机、每用户唯一、独立存储
绝对不要用
OLD_PASSWORD()
(已弃用)、
PASSWORD()
(5.7+ 移除)、
ENCRYPT()
(仅 crypt,弱且不可移植)

敏感字段加密后查询会变慢,而且不能索引

一旦对字段用了

AES_ENCRYPT()
,它就变成二进制 blob,无法走普通 B-tree 索引;想查「某个加密后的邮箱是否已存在」,只能全表扫描解密比对,性能断崖下跌。

若需模糊查询或范围查询,加密不是解法,应考虑字段级权限(
GRANT SELECT(col1, col2) ON db.tbl
)或行级安全(MySQL 8.0+ 的
CREATE POLICY
审计日志、备份导出等环节,加密字段仍以密文形式存在,意味着密钥管理必须覆盖整个数据生命周期——漏掉任意一环,保护就归零 MySQL 企业版有透明数据加密(TDE),但只加密 ibd 文件,不防应用层泄露;开源版无此功能 实际部署时,最常被忽略的是密钥轮换机制和
iv
的持久化方式——它们往往硬编码在 SQL 脚本里,或者靠人工维护,一出故障就全量数据无法还原。

相关推荐