MySQL不支持字段自动加密,需用AES_ENCRYPT/AES_DECRYPT函数手动实现,存入VARBINARY/BLOB字段,密钥需严格匹配,查询须UNHEX后解密,密文查法才可走索引,禁止硬编码密钥。

MySQL 本身不提供开箱即用的“字段自动加密”机制,但可以通过 AES_ENCRYPT 和 AES_DECRYPT 函数在应用层或 SQL 层实现字段级加密存储——这是目前社区版最可行、最常用的方式。
为什么不能直接 ALTER COLUMN ENCRYPT?
MySQL 没有类似 PostgreSQL 的 ENCRYPTED WITH 语法,也没有字段级透明加密开关。所谓“加密字段”,本质是把明文用函数转成二进制密文,再存进 VARBINARY 或 BLOB 字段。这一步必须由开发者显式调用函数完成,数据库不会帮你自动加解密。
-
AES_ENCRYPT()返回的是二进制数据,如果存进VARCHAR会乱码或截断,必须配VARBINARY(255)这类类型(长度要预留填充空间,比如手机号建议VARBINARY(128)) - 密钥(
key_str)参与运算,但函数本身不校验密钥长度或格式;用错密钥会导致AES_DECRYPT返回NULL,且无提示 - 字段加密后无法直接用于索引加速查询(除非建函数索引,MySQL 8.0.13+ 支持
CREATE INDEX idx ON t1 ( (AES_DECRYPT(encrypted_col, 'key')) ),但实际效果受限)
INSERT 和 SELECT 时怎么写才不出错?
常见错误是忘记类型匹配、漏掉 HEX/UNHEX、或在 WHERE 条件里用错比较方式。实操要点如下:
- 插入时:用
HEX(AES_ENCRYPT('13812345678', 'my_key'))存入VARBINARY字段,避免二进制数据被客户端截断 - 查询时:必须用
AES_DECRYPT(UNHEX(encrypted_col), 'my_key'),顺序不能颠倒(先UNHEX再DECRYPT) - WHERE 过滤:明文查法
WHERE AES_DECRYPT(UNHEX(encrypted_col), 'my_key') = '13812345678'会全表扫描;密文查法WHERE encrypted_col = HEX(AES_ENCRYPT('13812345678', 'my_key'))可走索引,但需确保密钥和算法完全一致 - 别硬编码密钥:生产环境应从会话变量(
@app_key)或配置表读取,禁止写死在 SQL 或存储过程中
自定义函数能简化调用吗?
可以封装,但要注意安全边界和性能代价。例如:
CREATE FUNCTION safe_encrypt(data VARCHAR(255), key_str VARCHAR(255)) RETURNS VARBINARY(255) DETERMINISTIC RETURN AES_ENCRYPT(data, key_str);
这类函数只是语法糖,不解决密钥泄露或字段类型错配问题。反而容易掩盖细节:比如没检查 data 是否为 NULL(AES_ENCRYPT(NULL, key) 返回 NULL,但 UNHEX(NULL) 会报错),也没处理密钥过短(AES 要求密钥至少 16 字节,太短会被静默补零)。
- 如果要用自定义函数,务必在函数内加
IF LENGTH(key_str) 校验 - 函数名别起
aes_encrypt,避免和内置函数重名导致行为不可预期 - 函数返回类型必须和字段类型严格一致,否则 INSERT 时可能触发隐式转换失败
加密后备份和权限怎么管?
加密不是万能锁。密钥一旦泄漏,所有密文瞬间可破;密钥丢了,数据就永久不可读。关键点很实在:
- 备份文件本身不加密,但密钥文件(如
keyring)必须单独加密并离线保存——它比数据更敏感 - 给 DBA 授予
SELECT权限时,避开加密字段;或用列权限限制只能查id、name,不给phone_enc查询权 -
AES_DECRYPT是函数,只要有 SELECT 权限 + 知道密钥,就能解出明文;所以密钥管理永远比 SQL 写法更重要 - 不要以为用了
AES_ENCRYPT就高枕无忧:如果应用层日志打印了原始 SQL,密钥可能随语句一起泄露
真正麻烦的从来不是怎么写那条 INSERT,而是密钥谁来管、存哪、轮换周期多长、审计日志记不记解密行为——这些没法靠 SQL 解决。


















