前言
数据安全已经是业务系统的硬性要求,尤其是用户手机号、身份证、银行卡、隐私合同这类敏感信息。很多开发同学一听到加密就直接上手 AES 写业务代码,却忽略 MySQL 本身提供的加密能力,也分不清传输加密、存储加密、字段加密三者的差异。
本文梳理 MySQL 加密体系,对比不同加密方案的适用场景、优缺点,附带可直接测试的 SQL 示例,帮你根据业务选型。
版本说明:示例基于 MySQL 8.0,5.7 部分函数略有差异,生产环境优先 8.0。
一、先分清三个层级的加密
很多人容易混淆,一句话区分:
- 传输加密(TLS/SSL) :客户端 ↔ MySQL 服务之间网络链路加密,防止抓包窃取传输中的 SQL 和返回结果。不加密数据库磁盘上的数据文件。
- 透明数据加密 TDE :磁盘文件加密。数据库文件、ibd 文件落盘就是密文,数据库正常读写不需要改业务 SQL。不对单独字段做区分,整个表空间加密。
- 字段级加密(列加密):只对指定敏感字段加密,普通字段明文存储。加密/解密逻辑在 SQL 层,查询时需要主动解密,粒度最细。
简单总结:SSL 管路上、TDE 管磁盘文件、字段加密管单条敏感数据。三者可以叠加使用,不是互斥。
二、传输加密 SSL/TLS:必开的基础防护
作用:防止在局域网、公网抓包,拦截到明文 SQL、返回数据。
MySQL 8.0 默认会自动生成证书,支持 SSL 连接,但是客户端默认不一定走 SSL。
查看当前 SSL 状态
sql
SHOW VARIABLES LIKE '%ssl%';
have_ssl 值为 YES 代表服务端支持 SSL。
查看当前连接是否使用加密:
sql
SHOW STATUS LIKE 'Ssl_cipher';
Ssl_cipher 不为空,代表当前连接启用 SSL。
客户端连接强制 SSL
连接串增加 ssl-mode=REQUIRED,MySQL8.0 推荐:
mysql -h 127.0.0.1 -P3306 -u root -p --ssl-mode=REQUIRED
优点 :配置简单,业务代码几乎不用改动,性能损耗极低。
缺点:仅保护传输过程,数据库文件本身仍是明文。DBA 直接登录数据库,依然可以看到所有明文。
建议:所有生产环境必须开启 SSL,这是底线,不能当作高级安全方案。
三、透明数据加密 TDE:整表空间加密
MySQL InnoDB TDE 是对独立表空间(.ibd 文件)加密。加密密钥由外部密钥管理服务或者文件密钥管理。
社区版 MySQL 8.0.13+ 支持 TDE,但企业版的密钥管理插件更完善;社区版一般使用 file-keyring 插件,仅适合测试。
开启 TDE 后:
- 磁盘上的 ibd 数据文件是密文,即使把硬盘或者ibd文件拷贝走,没有密钥无法恢复数据;
- 业务SQL完全不用修改,
SELECT、INSERT照常写,数据库引擎自动加解密; - 索引也会被加密。
优点 :业务无侵入,防护磁盘文件泄露。
缺点:
- 粒度粗,整张表加密,无法只加密某几个字段;
- 数据库正常登录的账号,查询依旧返回明文,不能防止内部DBA越权查看;
- 密钥管理是难点,密钥丢失 = 数据全部无法恢复;
- 社区版插件不适合高可用生产,推荐企业版密钥管理。
适用场景:服务器磁盘、存储介质防泄露,云服务器裸盘丢失防护。
四、字段级加密:敏感字段单独加密(业务最常用)
针对手机号、身份证这类少量敏感字段,只加密指定列,这是绝大多数业务的选择。
MySQL 提供两套方案:内置加密函数、InnoDB 列加密(MySQL 8.0.16+)。
方式1:内置 AES 函数(AES_ENCRYPT / AES_DECRYPT)
⚠️ 注意:MySQL内置AES函数不推荐直接生产裸用,密钥写在SQL里存在日志泄露风险,仅用于演示原理。
示例建表:
sql
CREATE TABLE user_info (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50),
id_card VARBINARY(256) -- 加密后是二进制,使用VARBINARY存储
);
插入加密数据:
sql
INSERT INTO user_info(username, id_card)
VALUES ('zhangsan', AES_ENCRYPT('110101199001011234', '你的加密密钥'));
查询解密:
sql
SELECT
id,
username,
AES_DECRYPT(id_card, '你的加密密钥') AS id_card_plain
FROM user_info;
问题
- 密钥写在SQL中,如果开启通用查询日志、binlog,密钥可能被记录;
- 无法对加密字段做索引直接模糊查询;加密后是密文,
like无法使用; - AES_ENCRYPT 返回二进制,不能用 VARCHAR,必须 VARBINARY;
- 密钥管理需要自己在应用层维护,不要硬编码到代码。
✅ 最佳实践:把加解密逻辑放到应用层(Java/Python),而不是MySQL函数 。
应用AES加密后,将密文存入MySQL,数据库只存密文,密钥完全不经过SQL,避免日志泄露密钥。
方式2:InnoDB 列加密(Column Level Encryption)
MySQL8.0.16 引入,原生字段加密。密钥由 MySQL 密钥环管理,密钥不会出现在 binlog、查询日志中,比 AES_ENCRYPT 更安全。
sql
CREATE TABLE user_secret (
id INT PRIMARY KEY AUTO_INCREMENT,
phone VARCHAR(20) ENCRYPTED,
email VARCHAR(100)
);
查询时自动解密,前提用户拥有解密权限。
优点:密钥不暴露在SQL语句,权限隔离,可以控制哪些账号能解密。
缺点:同样无法对密文字段做模糊检索;版本要求高,密钥组件配置复杂。
五、加密后查询的痛点:密文不能模糊搜索
这是开发最容易踩坑的地方:
一旦字段加密,无法直接 like '%关键词%'
解决方案选型:
- 等值查询场景 :使用哈希(SHA256)存储摘要。
原始手机号AES加密存密文;额外新增一列保存手机号SHA256哈希。查询时,前端传入手机号,同样计算哈希,等值匹配。 注意:哈希是单向不可逆,只能用于匹配,不能解密获取原文。 - 模糊查询场景 :
方案A:应用层解密全量数据后过滤(数据量大时性能极差)
方案B:保留脱敏明文索引字段(例如只保留手机号前3+后4位用于检索,中间掩码),不要存储完整明文。
原则:不要为了方便查询,把完整敏感信息明文保存。
六、不同加密方案对比表
| 方案 | 防护对象 | 业务侵入 | 粒度 | 能否模糊查询 |
|---|---|---|---|---|
| SSL传输加密 | 网络抓包 | 几乎无 | 连接级别 | 可以 |
| TDE表空间加密 | 磁盘文件泄露 | 无 | 整张表空间 | 可以 |
| MySQL AES函数字段加密 | 数据库内数据泄露 | 中等 | 字段 | 不可以 |
| 应用层AES加密 | 数据库内数据泄露 | 中等 | 字段 | 不可以 |
七、生产环境推荐落地方案
方案A(绝大多数Web业务推荐)
- 数据库连接强制开启 SSL;
- 敏感字段在应用代码(Python/Java)AES加密,密文存入MySQL VARBINARY;
- 密钥使用密钥管理服务(KMS),禁止硬编码在配置文件;
- 新增哈希字段用于等值查询;
- 数据库账号最小权限原则,普通业务账号没有多余权限。
方案B(云数据库,磁盘安全要求高)
在方案A基础上,开启数据库TDE,防止底层存储介质泄露。
❌ 不推荐:直接使用 MySQL AES_ENCRYPT 在SQL中传密钥,容易在binlog、慢查询日志泄露密钥。
八、常见误区避坑
-
误区:加密=可以防止DBA查看数据
TDE和SSL都不能防止有权限的DBA查询明文;只有应用层加密,数据库本身只存密文,即使DBA登录也看不到原始数据。
-
误区:加密后性能损耗巨大
TDE和SSL性能损耗很小;字段加密的性能开销主要来自应用层AES加解密,少量敏感字段基本无感,大批量数据才会明显。
-
误区:MD5可以加密存储隐私数据
MD5/SHA是哈希,不可逆,只能校验。不能用来"加密解密"原始身份证手机号,只适合做等值检索摘要。
-
误区:加密之后就万事大吉
加密只是安全一环,还要配合:最小账号权限、审计日志、脱敏展示、备份加密。备份文件如果是明文导出,加密就失去意义。
九、总结
MySQL 的加密体系分为链路、磁盘、字段三层,选型核心看你的风险点:
- 担心网络抓包:开SSL
- 担心数据库磁盘文件被盗:TDE
- 担心数据库内部人员越权查看敏感隐私:应用层字段加密
绝大多数业务,优先选择「SSL + 应用层AES加密敏感字段 + KMS密钥管理」,平衡安全、开发成本和性能。
加密不是万能银弹,密钥管理、权限控制、备份安全,缺一不可。