MySQL 完全指南 / 第 14 章:安全管理
第 14 章:安全管理
安全是数据库运维的底线。本章覆盖用户权限、审计、加密和 SQL 注入防护。
14.1 用户管理
14.1.1 创建用户
-- 创建本地用户
CREATE USER 'appuser'@'localhost' IDENTIFIED BY 'App!Str0ngPass123';
-- 创建允许远程访问的用户
CREATE USER 'appuser'@'192.168.1.%' IDENTIFIED BY 'App!Str0ngPass123';
-- 创建允许任意主机访问的用户(慎用)
CREATE USER 'appuser'@'%' IDENTIFIED BY 'App!Str0ngPass123';
-- MySQL 8.0 默认认证插件
CREATE USER 'appuser'@'%'
IDENTIFIED WITH caching_sha2_password BY 'App!Str0ngPass123'
PASSWORD EXPIRE INTERVAL 90 DAY -- 90天过期
FAILED_LOGIN_ATTEMPTS 3 -- 3次失败锁定
PASSWORD_LOCK_TIME 2; -- 锁定2天
14.1.2 密码策略
-- 查看密码策略
SHOW VARIABLES LIKE 'validate_password%';
-- 设置密码策略
SET GLOBAL validate_password.policy = MEDIUM; -- LOW/MEDIUM/STRONG
SET GLOBAL validate_password.length = 12; -- 最短12位
SET GLOBAL validate_password.mixed_case_count = 1; -- 至少1个大小写
SET GLOBAL validate_password.number_count = 1; -- 至少1个数字
SET GLOBAL validate_password.special_char_count = 1; -- 至少1个特殊字符
-- 修改密码
ALTER USER 'appuser'@'%' IDENTIFIED BY 'New!Pass123456';
-- 修改密码认证插件(兼容旧客户端)
ALTER USER 'appuser'@'%' IDENTIFIED WITH mysql_native_password BY 'New!Pass123456';
14.1.3 用户管理操作
-- 查看所有用户
SELECT User, Host, plugin, authentication_string, password_lifetime
FROM mysql.user ORDER BY User;
-- 重命名用户
RENAME USER 'oldname'@'%' TO 'newname'@'%';
-- 删除用户
DROP USER 'appuser'@'%';
-- 锁定/解锁用户
ALTER USER 'appuser'@'%' ACCOUNT LOCK;
ALTER USER 'appuser'@'%' ACCOUNT UNLOCK;
14.2 权限管理
14.2.1 权限类型
| 权限 |
说明 |
范围 |
| ALL PRIVILEGES |
所有权限(不含 GRANT) |
全局/库/表 |
| SELECT |
查询 |
库/表/列 |
| INSERT |
插入 |
库/表/列 |
| UPDATE |
更新 |
库/表/列 |
| DELETE |
删除 |
库/表/列 |
| CREATE |
创建数据库/表 |
全局/库 |
| DROP |
删除数据库/表 |
全局/库 |
| ALTER |
修改表结构 |
全局/库 |
| INDEX |
创建/删除索引 |
库/表 |
| EXECUTE |
执行存储过程 |
全局/库 |
| GRANT OPTION |
授权权限 |
全局/库/表 |
| PROCESS |
查看线程 |
全局 |
| REPLICATION SLAVE |
从库复制 |
全局 |
| RELOAD |
FLUSH 操作 |
全局 |
14.2.2 GRANT 授权
-- 授予单库读写权限
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp.* TO 'appuser'@'%';
FLUSH PRIVILEGES;
-- 授予只读权限
GRANT SELECT ON myapp.* TO 'readonly'@'%';
-- 授予指定表权限
GRANT SELECT, INSERT ON myapp.users TO 'usermgr'@'%';
-- 授予指定列权限
GRANT SELECT (username, email, created_at) ON myapp.users TO 'limited'@'%';
-- 授予全局权限
GRANT PROCESS, REPLICATION SLAVE ON *.* TO 'monitor'@'%';
-- 授予带 GRANT OPTION 的权限
GRANT ALL PRIVILEGES ON myapp.* TO 'admin'@'localhost' WITH GRANT OPTION;
-- 查看权限
SHOW GRANTS FOR 'appuser'@'%';
SELECT * FROM information_schema.USER_PRIVILEGES WHERE GRANTEE LIKE "'appuser'%";
-- 撤销权限
REVOKE DELETE ON myapp.* FROM 'appuser'@'%';
REVOKE ALL PRIVILEGES ON *.* FROM 'appuser'@'%';
14.2.3 权限最佳实践
| 角色 |
权限 |
说明 |
| 应用用户 |
SELECT, INSERT, UPDATE, DELETE |
最小权限原则 |
| 只读用户 |
SELECT |
报表、分析 |
| DBA |
ALL PRIVILEGES + GRANT OPTION |
管理员 |
| 复制用户 |
REPLICATION SLAVE |
从库复制 |
| 监控用户 |
PROCESS, SELECT on performance_schema |
监控 |
⚠️ 最小权限原则:只授予必要的权限,避免使用 GRANT ALL ON *.*。
14.3 角色(MySQL 8.0+)
-- 创建角色
CREATE ROLE 'app_read', 'app_write', 'app_admin';
-- 为角色授权
GRANT SELECT ON myapp.* TO 'app_read';
GRANT INSERT, UPDATE, DELETE ON myapp.* TO 'app_write';
GRANT ALL PRIVILEGES ON myapp.* TO 'app_admin';
-- 将角色授予用户
GRANT 'app_read' TO 'report_user'@'%';
GRANT 'app_read', 'app_write' TO 'appuser'@'%';
GRANT 'app_admin' TO 'dba'@'%';
-- 设置默认角色
SET DEFAULT ROLE 'app_read', 'app_write' TO 'appuser'@'%';
-- 查看角色
SELECT * FROM mysql.role_edges;
SHOW GRANTS FOR 'appuser'@'%' USING 'app_read', 'app_write';
14.4 审计
14.4.1 General Log(通用日志)
-- 开启通用日志(记录所有 SQL,生产慎用)
SET GLOBAL general_log = ON;
SET GLOBAL general_log_file = '/var/log/mysql/general.log';
-- 查看日志位置
SHOW VARIABLES LIKE 'general_log%';
14.4.2 Audit Log 插件
# MySQL Enterprise Audit(企业版)
# 或使用开源替代:MariaDB Audit Plugin
-- 安装审计插件
INSTALL PLUGIN audit_log SONAME 'audit_log.so';
-- 配置审计策略
SET GLOBAL audit_log_policy = ALL; -- ALL/LOGINS/QUERIES/NONE
SET GLOBAL audit_log_format = JSON; -- OLD/NEW/JSON/CSV
14.4.3 连接审计
-- 查看当前连接
SELECT * FROM information_schema.PROCESSLIST;
-- 查看历史连接(Performance Schema)
SELECT
USER, HOST,
COUNT_STAR AS total_connections,
SUM_ROWS_EXAMINED AS total_rows_examined
FROM performance_schema.events_statements_summary_by_user_by_event_name
GROUP BY USER, HOST;
14.5 数据加密
14.5.1 传输加密(SSL/TLS)
-- 查看 SSL 状态
SHOW VARIABLES LIKE '%ssl%';
-- 创建要求 SSL 的用户
CREATE USER 'secure_user'@'%' IDENTIFIED BY 'Pass123!' REQUIRE SSL;
-- 连接时使用 SSL
mysql -u secure_user -p --ssl-mode=REQUIRED
-- 查看当前连接的 SSL 状态
SHOW STATUS LIKE 'Ssl_cipher';
14.5.2 存储加密(TDE)
-- InnoDB 表空间加密(MySQL 8.0+)
-- 1. 配置密钥环
[mysqld]
early-plugin-load=keyring_file.so
keyring_file_data=/var/lib/mysql-keyring/keyring
-- 2. 创建加密表
CREATE TABLE sensitive_data (
id BIGINT PRIMARY KEY,
ssn VARCHAR(20)
) ENCRYPTION='Y';
-- 3. 加密已有表
ALTER TABLE users ENCRYPTION='Y';
14.5.3 应用层加密
-- AES 加密/解密
-- 加密
INSERT INTO users (username, email, phone)
VALUES ('zhangsan',
HEX(AES_ENCRYPT('zhangsan@example.com', 'secret_key')),
HEX(AES_ENCRYPT('13800000001', 'secret_key')));
-- 解密
SELECT username,
AES_DECRYPT(UNHEX(email), 'secret_key') AS email,
AES_DECRYPT(UNHEX(phone), 'secret_key') AS phone
FROM users;
14.6 SQL 注入防护
14.6.1 注入原理
-- ❌ 不安全:直接拼接 SQL
-- 应用代码:SELECT * FROM users WHERE username = '${input}'
-- 输入:' OR '1'='1
-- 实际执行:SELECT * FROM users WHERE username = '' OR '1'='1'
-- 结果:返回所有用户
14.6.2 防护方法
-- ✅ 使用参数化查询(Prepared Statement)
PREPARE stmt FROM 'SELECT * FROM users WHERE username = ?';
SET @username = 'zhangsan';
EXECUTE stmt USING @username;
DEALLOCATE PREPARE stmt;
-- ✅ 应用层使用 ORM 或参数化查询
-- Java (JDBC): PreparedStatement
-- Python: cursor.execute("SELECT * FROM users WHERE username = %s", (username,))
-- Go: db.Query("SELECT * FROM users WHERE username = ?", username)
-- PHP: PDO prepared statements
14.6.3 其他防护措施
-- 1. 最小权限原则
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp.* TO 'app'@'%';
-- 不要给 DROP, ALTER, GRANT 等权限
-- 2. 输入验证
-- 应用层对所有用户输入进行验证和转义
-- 3. 关闭 local_infile
SET GLOBAL local_infile = OFF;
-- 4. 使用 Web 应用防火墙(WAF)
业务场景
场景 1:多环境权限方案
| 环境 |
用户 |
权限 |
| 开发环境 |
dev_user |
ALL on dev_db |
| 测试环境 |
test_user |
SELECT, INSERT, UPDATE, DELETE on test_db |
| 生产应用 |
app_user |
SELECT, INSERT, UPDATE, DELETE on prod_db |
| 生产只读 |
readonly_user |
SELECT on prod_db |
| DBA |
dba_user |
ALL PRIVILEGES with GRANT OPTION |
扩展阅读