mysql如何定期检查权限风险_mysql安全审计思路

来源:这里教程网 时间:2026-02-28 20:37:50 作者:

如何用
SELECT
快速识别高危账号

MySQL 权限风险往往藏在「看似正常」的账号里。重点不是查谁有

GRANT OPTION
,而是查谁拥有能绕过常规限制的能力。直接运行以下语句找出危险组合:

SELECT user, host, 
  IF(Select_priv='Y',1,0) + 
  IF(Insert_priv='Y',1,0) + 
  IF(Update_priv='Y',1,0) + 
  IF(Delete_priv='Y',1,0) AS dml_count,
  IF(Grant_priv='Y',1,0) AS has_grant,
  IF(Super_priv='Y',1,0) AS has_super,
  IF(Repl_slave_priv='Y',1,0) AS has_repl_slave
FROM mysql.user 
WHERE (dml_count >= 3 OR has_grant = 1 OR has_super = 1 OR has_repl_slave = 1);

注意:

mysql.user
表权限字段是字符型(
'Y'
/
'N'
),不能直接用
= 1
dml_count
是计算列,实际执行需拆成子查询或用
HAVING
过滤。更稳妥写法:

SELECT user, host, 
  Grant_priv, Super_priv, Repl_slave_priv,
  CONCAT('SHOW GRANTS FOR ''', user, '''@''', host, ''';') AS review_cmd
FROM mysql.user 
WHERE Grant_priv='Y' OR Super_priv='Y' OR Repl_slave_priv='Y';
Super_priv='Y'
意味着可 kill 任意连接、修改全局变量、绕过
sql_mode
限制
Repl_slave_priv='Y'
常被忽略——它允许伪造主从身份,配合
LOAD DATA LOCAL INFILE
可读取服务器任意文件
账号
host
'%'
且权限宽松时,必须立即收缩范围,哪怕只改一个
host
字段也能大幅降低攻击面

为什么
SHOW GRANTS
结果不能直接信

SHOW GRANTS FOR 'u'@'h'
只显示显式授予的权限,不反映角色继承、动态权限(MySQL 8.0+)、或通过
PROXY
机制获得的间接权限。真实权限是叠加结果。

检查角色权限要分两步:

SELECT * FROM mysql.role_edges WHERE TO_HOST = 'your_role_name';
SELECT * FROM mysql.role_edges WHERE FROM_HOST = 'your_role_name';

而动态权限(如

BACKUP_ADMIN
CLONE_ADMIN
)不会出现在
mysql.user
表中,得查:

SELECT * FROM mysql.role_edges 
JOIN mysql.default_roles USING (FROM_HOST, FROM_HOST)
WHERE TO_HOST IN (SELECT user FROM mysql.user WHERE user='target_user');
MySQL 8.0.16+ 才支持动态权限,老版本查
INFORMATION_SCHEMA.APPLICABLE_ROLES
会报错
SHOW GRANTS
不显示
SYSTEM_VARIABLES_ADMIN
等新权限,但它们能让用户修改
secure_file_priv
log_bin
,直接影响安全边界
如果账号被赋予了角色,又单独授了同名权限(比如
SELECT
),撤销角色不会自动撤掉该权限——得手动清理残留

自动化审计脚本该监控哪些变更点

权限风险不是静态的,关键在于「谁在什么时候改了什么」。MySQL 自身不记录细粒度 DCL 日志,必须靠外部手段捕获:

开启
general_log
并过滤含
GRANT
REVOKE
CREATE USER
DROP USER
的行(仅临时启用,性能代价大)
performance_schema
监控语句事件(MySQL 5.7+):
SELECT THREAD_ID, EVENT_NAME, SQL_TEXT 
FROM performance_schema.events_statements_history_long 
WHERE SQL_TEXT REGEXP '^(GRANT|REVOKE|CREATE USER|DROP USER)';
生产环境推荐:在代理层(如 ProxySQL、MaxScale)或接入网关统一拦截 DCL 语句,打标后发到 SIEM

真正要告警的不是「有 GRANT」,而是:

非运维时段(如凌晨 2–4 点)出现
GRANT ALL ON *.*
普通业务账号突然被赋予
REPLICATION SLAVE
同一账号在 5 分钟内连续执行超过 3 次
GRANT

权限回收时最常踩的三个坑

删权限不是删数据,操作不当会导致服务中断或权限残留:

REVOKE INSERT ON db.* FROM 'u'@'%'
不会清除该用户从角色继承的
INSERT
权限——必须先
REVOKE role FROM 'u'@'%'
,再
REVOKE
显式权限
DROP USER 'u'@'%'
在 MySQL 8.0.12+ 默认级联删除角色授权,但若用户是某角色的
ADMIN OPTION
持有者,角色本身不会被删,只是失去管理权
mysqldump --no-data --routines --triggers mysql
备份权限表时,
mysql.role_edges
mysql.default_roles
不会被包含,必须显式导出

最小权限原则不是一句口号:每次回收前,用

SELECT COUNT(*) FROM information_schema.TABLE_PRIVILEGES
确认当前库表级权限总数,回收后再比对,避免误删导致应用报
ERROR 1142 (42000)

相关推荐