mysql 8.0.14+ 可通过 performance_schema 启用账户管理审计:先确认 performance_schema 已开启,再启用 statement/abstract/account_management 仪器及 statements 相关消费者,最后查询 events_statements_history_long 中 grant 语句并结合 user、host、sql_text 分析权限变更。

如何开启 MySQL 的权限变更审计日志
MySQL 默认不记录 GRANT、CREATE USER、DROP USER 这类敏感操作,必须主动启用审计能力。5.7+ 企业版自带 Audit Log Plugin,但社区版得靠 general_log 或 slow_query_log 间接捕获——效果差、开销大、还容易漏掉权限语句。
更可行的方案是启用 mysql.sys 提供的语句级审计(需 8.0.14+)或用 performance_schema 持续抓取 account_management 类事件:
- 确认已开启:
SELECT VARIABLE_VALUE FROM performance_schema.global_variables WHERE VARIABLE_NAME = 'performance_schema'返回ON - 启用账户管理事件采集:
UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' WHERE NAME = 'statement/abstract/account_management' - 确保相关消费者已打开:
UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME LIKE '%statements%或%events_statements%
从 performance_schema 查异常 GRANT 操作
权限提升尝试往往表现为非 DBA 账户对高权限语句的执行,比如普通用户突然运行 GRANT SUPER ON *.* TO ...。这类行为不会报错(只要语法合法),但会在 performance_schema.events_statements_history_long 留下痕迹。
关键过滤条件不是看「有没有 GRANT」,而是看「谁在什么时间对谁授了什么权限」:
- 查最近 1 小时内所有
GRANT语句:SELECT EVENT_ID, USER, HOST, SQL_TEXT, TIMER_START FROM performance_schema.events_statements_history_long WHERE SQL_TEXT LIKE 'GRANT%' AND TIMER_START > UNIX_TIMESTAMP(NOW() - INTERVAL 1 HOUR) * 1000000000 - 重点检查
HOST字段是否为非预期 IP(如'%'、'10.0.0.%'),以及SQL_TEXT是否包含SUPER、REPLICATION CLIENT、FILE等高危权限 -
TIMER_START是纳秒级时间戳,别直接用NOW()对比,要用UNIX_TIMESTAMP换算
为什么 general_log 不适合做权限审计
general_log 看起来最直接,但它记录的是连接建立和每条语句的原始输入,没有上下文关联——你无法知道这条 GRANT 是哪个应用、哪个线程、以哪个身份发起的,更没法区分是人肉操作还是被注入的 SQL。
它还会带来明显副作用:
- 日志体积爆炸:每个查询都写盘,尤其在高并发下,IO 压力陡增
- 权限泄露风险:
general_log可能记录明文密码(如SET PASSWORD FOR 'u'@'h' = 'xxx'),且默认权限宽松,任意有SELECT权限的用户都可能读到 - 无法过滤事件类型:
general_log不区分 DDL/DML/权限语句,查一条GRANT得扫全量日志,基本不可运维
权限提升尝试的典型误报与漏报场景
真实环境中,GRANT 本身不是攻击,问题在于「谁、何时、为何、对谁」。自动告警容易在这几处翻车:
- 漏报:应用使用代理账号(如
app_proxy@'10.0.1.%')统一执行授权,但实际权限由后端服务动态生成——这种行为合法,但日志里看不出调用链,容易当成异常 - 误报:DBA 在凌晨批量刷新权限,
HOST是跳板机 IP,USER是运维账号,但没加注释,监控一概标红 - 绕过检测:攻击者用
PREPARE/EXECUTE动态拼接GRANT语句,SQL_TEXT显示为EXECUTE @stmt,原始权限内容藏在events_statements_history_long的前置SET @stmt = 'GRANT...'行里,不连查会丢关键信息
真正难的不是查出 GRANT,而是把一次授权动作还原成完整意图:调用方、触发条件、影响范围。这需要把 performance_schema 数据和你的 CMDB、部署流水线日志对齐——否则永远在猜。











