首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >你的 MySQL 数据库真的安全吗?表空间加密一招防硬盘泄露

你的 MySQL 数据库真的安全吗?表空间加密一招防硬盘泄露

作者头像
俊才
发布2026-01-19 13:49:14
发布2026-01-19 13:49:14
4640
举报
文章被收录于专栏:数据库干货铺数据库干货铺

当今数据安全合规要求日益严格的环境下(如 GDPR、等保2.0、金融行业监管等),仅仅依靠网络传输加密(如 TLS)和访问控制已不足以保障数据安全。一旦攻击者获取数据库服务器的物理访问权限(例如盗取硬盘、虚拟机镜像泄露、备份文件外泄),未加密的数据库文件(如 .ibd、.frm)可能直接暴露敏感信息。

为应对这一风险,MySQL 自 5.7.11 版本起正式支持 InnoDB 表空间静态加密(Data-at-Rest Encryption)。该功能可在数据写入磁盘前自动加密,在读取时自动解密,对应用程序完全透明,是构建纵深防御体系的重要一环。

1. InnoDB 表空间加密的基本原理

InnoDB 的加密机制采用两级密钥体系(Two-tier Key Architecture):

  • 表空间密钥(Tablespace Key):每个独立表空间(.ibd 文件)拥有一个唯一的 AES 加密密钥,用于加解密该表的数据页。
  • 主密钥(Master Key):所有表空间密钥均使用同一个主密钥进行加密后存储在表空间头部。主密钥本身由 密钥环插件(Keyring Plugin) 管理,如 keyring_file、keyring_okv 或企业级 HSM 方案。

优势:

  • 主密钥轮换时无需重加密整个表数据,仅需重新加密表空间密钥,效率极高
  • 即使攻击者拿到 .ibd 文件,若无主密钥,也无法还原表空间密钥,数据仍不可读

2. 配置加密插件

2.1 修改配置文件

在mysql配置文件【mysqld】选项中添加如下内容(注意插件的路径及权限)

代码语言:javascript
复制
plugin_dir=/usr/local/mysql5.7/lib/mysql/plugin     #  插件路径,根据实际情况修改
early-plugin-load="keyring_file.so"                 # 加密插件
keyring_file_data=/data/mysql/mysql3307/keyring/keyring  # 路径不存在,需要创建,需要创建
innodb_file_per_table=1                   # 只作用于独立表空间

2.2 创建加密所需的路径并配置权限

创建时要注意 ,keyring_file_data 里配置的keyring会在启动时自动创建,本步骤中创建到对应目录即可

代码语言:javascript
复制
mkdir -p   /data/mysql/mysql3307/keyring/
chown -R mysql:mysql  /data/mysql/mysql3307/keyring/
chmod 750 /data/mysql/mysql3307/keyring

2.3 重启MySQL

重启mysql即可,启动后会发现注意/data/mysql3306/keyring 目录下生成了 keyring文件

注意,重启后也要看一下mysql错误日志里有没有相关错误信息,如果没有错误则继续进行

2.4 查看插件状态

启动后可以查看插件是否生效

代码语言:javascript
复制
mysql> SELECT PLUGIN_NAME, PLUGIN_STATUS, PLUGIN_Type,PLUGIN_Library FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME LIKE 'keyring_file';
+--------------+---------------+-------------+-----------------+
| PLUGIN_NAME  | PLUGIN_STATUS | PLUGIN_Type | PLUGIN_Library  |
+--------------+---------------+-------------+-----------------+
| keyring_file | ACTIVE        | KEYRING     | keyring_file.so |
+--------------+---------------+-------------+-----------------+
1 row in set (0.00 sec)
mysql> 

或者用 show plugins命令查看

3. 测试加密表空间

3.1 创建加密的新表

创建一张新表,并添加ENCRYPTION='Y' ,加密表空间

代码语言:javascript
复制
mysql> create table  test1(
id int  primary key auto_increment,
name varchar(20),
key name(name))
 ENCRYPTION='Y';
Query OK, 0 rows affected (0.02 sec)

此时,keyring文件也会有变化

3.2 新增数据

向新增的测试表里添加测试数据,并查看

代码语言:javascript
复制
mysql> insert into  test1(id,name) values(1,'anm'),(2,'keyring');
Query OK, 2 rows affected (0.01 sec)
Records: 2  Duplicates: 0  Warnings: 0
mysql> select  * from test1;
+----+---------+
| id | name    |
+----+---------+
|  1 | anm     |
|  2 | keyring |
+----+---------+
2 rows in set (0.00 sec)

3.3 修改是否加密

测试取消表空间加密

代码语言:javascript
复制
mysql> show  create table test1;
+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                                                                          |
+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| test1 | CREATE TABLE `test1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 ENCRYPTION='Y' |
+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> alter table test1  ENCRYPTION='N';
Query OK, 2 rows affected (0.04 sec)
Records: 2  Duplicates: 0  Warnings: 0
mysql> show  create table test1;
+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                                                                          |
+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| test1 | CREATE TABLE `test1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 ENCRYPTION='N' |
+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> select  * from test1;
+----+---------+
| id | name    |
+----+---------+
|  1 | anm     |
|  2 | keyring |
+----+---------+
2 rows in set (0.00 sec)

再配置为加密

代码语言:javascript
复制
mysql> alter table test1  ENCRYPTION='Y';
Query OK, 2 rows affected (0.03 sec)
Records: 2  Duplicates: 0  Warnings: 0
mysql> select  * from test1;
+----+---------+
| id | name    |
+----+---------+
|  1 | anm     |
|  2 | keyring |
+----+---------+
2 rows in set (0.00 sec)

所以,表空间加密方式是可以在线调整的,且对数据查询不影响。

另外,keyring_file_data也是可以动态调整的,比较简单,就不演示了

3.4 统计表空间加密的表

想要知道哪些表的表空间加密了,可以通过数据字典表里查看

代码语言:javascript
复制
mysql>  SELECT TABLE_SCHEMA, TABLE_NAME, CREATE_OPTIONS FROM INFORMATION_SCHEMA.TABLES WHERE  table_schema='testdb' and CREATE_OPTIONS='ENCRYPTION="Y"';
+--------------+------------+----------------+
| TABLE_SCHEMA | TABLE_NAME | CREATE_OPTIONS |
+--------------+------------+----------------+
| testdb       | test1      | ENCRYPTION="Y" |
+--------------+------------+----------------+
1 row in set (0.01 sec)
mysql> 

4 . 异常处理

如果keyring文件损坏或被误删除了,会出现什么情况

4.1 备份keyring文件

为了保险起见,先备份一下keyring文件

代码语言:javascript
复制
[root@alidb mysql3307]# cd keyring/
[root@alidb keyring]# ll
total 4
-rw-r----- 1 mysql mysql 155 Jan  7 20:23 keyring
[root@alidb keyring]# cp keyring keyring.bak

4.2 删除keyring

直接删除keyring文件

代码语言:javascript
复制
[root@alidb keyring]# rm -f keyring
[root@alidb keyring]# ll -h 
total 4.0K
-rw-r----- 1 root root 155 Jan  7 20:28 keyring.bak

4.3 查看数据是否正常

查看数据及新建加密表是否成功

代码语言:javascript
复制
mysql> select  * from test1;
+----+---------+
| id | name    |
+----+---------+
|  1 | anm     |
|  2 | keyring |
+----+---------+
2 rows in set (0.00 sec)
mysql> create table  test2(id int  primary key auto_increment, name varchar(20),key name(name)) ENCRYPTION='Y';
Query OK, 0 rows affected (0.01 sec)

也就是说此时,即使keyrig文件丢失也是可以正常操作的,因为MYSQL进程还占用着文件,实际没删除。

3.4 重启数据库

重启数据库后,会发现,又自动生成了keyring文件

此时再查看加密表

代码语言:javascript
复制
mysql> select  * from test1;
ERROR 3185 (HY000): Can't find master key from keyring, please check in the server log if a keyring plugin is loaded and initialized successfully.

创建加密表

代码语言:javascript
复制
mysql> create table  test2(id int  primary key auto_increment, name varchar(20),key name(name)) ENCRYPTION='Y';
Query OK, 0 rows affected (0.02 sec)

新建表是可以的,因为此时相当于初始化的时候。

那么再将原keyring还原,然后再重启数据库,会发现又能成功了

代码语言:javascript
复制
mysql> select  * from test1;
+----+---------+
| id | name    |
+----+---------+
|  1 | anm     |
|  2 | keyring |
+----+---------+
2 rows in set (0.00 sec)

由此可见,keyring 文件中存储的是主密钥的加密形式,一旦该文件丢失且无备份,即使拥有完整的 .ibd 数据文件,也无法解密任何加密表——数据将永久不可恢复。,因此要定期备份 keyring 文件,并与数据库逻辑/物理备份分开存储

5. MySQL 8.0 的加密增强

MYSQL8.0开始,安全性增强了很多,操作也有很多变化。和5.7版本相比,主要有如下变化:

功能

MySQL 5.7(≥5.7.11)

MySQL 8.0

✅ InnoDB 表空间加密

支持(独立表空间)

增强支持,性能更优

🔑 密钥环插件(Keyring)

keyring_file、keyring_okv(企业版)

新增 keyring_aws、keyring_hashicorp 等云原生插件

🔄 主密钥轮换

ALTER INSTANCE ROTATE INNODB MASTER KEY

同左,但更稳定高效

📦 Redo/Undo 日志加密

不支持

8.0.30+ 支持 Redo/Undo 日志加密(需配置)

📜 Binlog 加密

不支持

8.0.14+ 支持 Binlog 加密(依赖 keyring)

🔐 默认认证插件

mysql_native_password

caching_sha2_password(更安全)

🛡️ 透明数据加密(TDE)

部分支持(仅表空间)

完善的 TDE 体系(含日志、备份)

🌐 TLS 支持

TLS 1.2

TLS 1.3(8.0.16+),更强传输安全

5.1 Binlog 加密(8.0.14+)

  • 痛点解决:在主从复制或 CDC 场景中,Binlog 文件可能包含敏感操作(如 INSERT INTO users VALUES ('张三', '138****1234')),若被窃取可直接还原数据。
  • 启用方式:
代码语言:javascript
复制
[mysqld]
binlog_encryption = ON
  • 依赖:必须启用 keyring 插件(如 keyring_file 或云 KMS)。
  • 效果:所有新生成的 binlog 文件(.000001 等)自动加密,旧文件不受影响。
  • 适用场景:金融交易日志、审计追踪、跨机房同步等高敏环境。

5.2 Redo/Undo 日志加密(8.0.30+)

  • 意义重大:Redo Log 记录了所有数据变更,Undo Log 用于事务回滚,二者均可能包含明文敏感数据。
  • 配置:
代码语言:javascript
复制
[mysqld]
innodb_redo_log_encrypt = ON
innodb_undo_log_encrypt = ON

注意: 启用后首次启动会重建 redo/undo 文件,需预留停机窗口。 ⚠️ 此功能在 5.7 中完全缺失,是 8.0 在“全链路静态加密”上的关键突破。

5.3 更丰富的 Keyring 插件生态

MySQL 8.0 官方扩展了对现代密钥管理系统的原生支持:

代码语言:javascript
复制
直接对接 AWS KMS,适合云上部署

优势:避免将主密钥以明文形式存于数据库服务器,符合“密钥与数据分离”安全原则。

5.4 加密函数增强(应用层辅助)

  • MySQL 8.0 新增或改进了多个加密函数,便于应用层实现字段级加密:
  • AES_ENCRYPT() / AES_DECRYPT():支持指定块加密模式(如 CBC、ECB)
  • CREATE_ENCRYPTION_KEY()(企业版):动态生成会话密钥

支持 RANDOM_BYTES() 生成高质量随机数 可与表空间加密互补,实现“字段级 + 存储级”双重保护。

5.5. 性能影响

MySQL 8.0 的 InnoDB 表空间加密(Transparent Data Encryption, TDE)在提供强大数据保护能力的同时,确实会带来一定的性能开销。但得益于底层优化,其影响通常非常有限

6. 小结

InnoDB 表空间加密的核心价值在于实现 “静态数据保护(Data-at-Rest Encryption)” ——即使数据库文件(如 .ibd)被非法拷贝、硬盘被盗或备份泄露,攻击者也无法直接读取其中的敏感信息。它对应用完全透明,无需修改业务代码,却能显著提升数据库的安全水位。

在 MySQL 8.0 中,配合 Binlog、Redo Log 等日志加密能力,表空间加密已成为满足等保、GDPR、金融合规等安全要求的关键技术手段。启用成本低、性能影响小、防护效果强,是现代数据库安全架构中不可或缺的一环。

简言之:防的是“物理接触”,守的是“数据底线”。

大家平时还有哪些数据库问题,可以关注我的微信公众号“数据库干货铺”,一起探索进步,感谢。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-01-07,如有侵权请联系 cloudcommunity@tencent.com 删除
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档