应直接查询performance_schema.events_statements_history_long中非dba账户执行的grant/revoke语句,结合server_audit插件捕获query_ddl事件,并监控mysql.user表变更、文件写入及系统调用,实现多维度越权行为检测。

直接看 performance_schema.events_statements_history_long 里非预期账户执行的高危语句,比等告警更早发现越权苗头。
查谁在执行 GRANT/REVOKE 但不是 DBA 账户
权限提升往往不报错,只留下一条合法语法的 GRANT 记录。重点不是“有没有 GRANT”,而是“谁在执行”:
SELECT USER, HOST, SQL_TEXT, TIMER_START FROM performance_schema.events_statements_history_long WHERE SQL_TEXT LIKE 'GRANT%' AND USER NOT IN ('dba_admin', 'backup_user') AND TIMER_START > UNIX_TIMESTAMP(NOW() - INTERVAL 1 HOUR) * 1000000000- 注意过滤掉已知运维账号(如
'dba_admin'@'192.168.1.%'),只留可疑主体 -
HOST字段出现'%'、'10.0.0.%'或陌生公网 IP,比USER名更值得警惕 - 别用
NOW()直接比对TIMER_START——它单位是纳秒,必须换算
为什么不能只依赖 general_log
general_log 看起来能抓到所有语句,但实际用于越权监控时几乎无效:
- 日志里没有结构化字段,
user和host是混在连接行里的,和后续GRANT行不关联 - 中间件(如 ProxySQL)转发后,
user记的是中间件账号,不是真实操作人 - 日志里明文记密码(比如
CREATE USER 'x'@'%' IDENTIFIED BY 'pwd123'),生产环境开启等于自曝风险 - 每小时几百 MB 日志,grep 慢、轮转难、磁盘 I/O 压力大,且无法区分成功/失败
用 server_audit 插件捕获结构化权限事件
MySQL 社区版唯一靠谱的权限审计方案,前提是插件已安装并正确启用:
- 先确认插件存在:
SHOW VARIABLES LIKE 'plugin_dir';,然后检查该路径下是否有server_audit.so - 必须显式启用
QUERY_DDL:执行SET GLOBAL server_audit_events = 'CONNECT,QUERY_DDL';,漏掉这句就收不到GRANT - 日志路径建议独立设置:
SET GLOBAL server_audit_file_path = '/var/log/mysql/audit.log';,再chown mysql:mysql /var/log/mysql - 禁止和
general_log共存——两者叠加会显著拖慢写入,且日志内容互相干扰
绕过权限表修改的隐性越权行为
有些越权不走 GRANT,而是直接改 mysql.user 表或利用 UDF、LOAD DATA 注入,这类更难捕获:
- 定期比对
SELECT User, Host, authentication_string FROM mysql.user的哈希值,发现意外变更立即告警 - 监控
performance_schema.file_summary_by_instance中对/var/lib/mysql/mysql/user.MYD(或对应存储引擎文件)的写入 - 启用
auditd规则监听mysqld进程对敏感系统调用(如openat+O_WRONLY)的使用 - 任何通过
UPDATE mysql.user SET Super_priv='Y'的操作,都不会出现在events_statements_history_long中——因为那是内部表更新,不是用户 SQL
真正危险的越权往往没走标准语法路径,所以不能只盯 GRANT 关键词。要同时看权限表变更、进程行为、文件访问三类信号,缺一不可。











