第 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('[email protected]', '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 |
扩展阅读