不能直接依赖 binlog 做权限变更审计,因其在 row 格式下不记录 grant/revoke,statement 格式又缺执行者、ip 等关键字段;应启用 audit_log 插件或通过 proxysql 等外部代理实现。

MySQL 权限变更审计靠 binlog 能行吗?
不能直接依赖 binlog 做权限变更审计。binlog 记录的是 DML 和部分 DDL,但 GRANT、REVOKE 语句在 row 格式下不写入 binlog(MySQL 5.7+ 默认为 ROW),即使设为 STATEMENT 格式,也仅记录语句本身,不包含执行者、时间戳、客户端 IP 等审计关键字段。
真正可用的路径只有两个:开启 MySQL 自带的 audit_log 插件,或通过外部代理(如 ProxySQL)+ 日志解析实现。
启用 audit_log 插件必须注意的 4 个前提
MySQL 官方企业版自带 audit_log,社区版需手动安装插件(如 Percona Server 或 MariaDB 的等效插件)。以 MySQL 8.0 社区版 + Percona Server 为例:
-
audit_log插件文件名通常是audit_log.so(Linux)或audit_log.dll(Windows),需确认路径存在且有执行权限 - 必须在
my.cnf中显式加载:plugin_load_add = audit_log.so,仅用INSTALL PLUGIN动态加载无法持久化 - 插件默认只记录连接/断开和查询事件,要捕获权限变更,必须设置:
audit_log_policy = ALL或至少audit_log_policy = CONTROLS - 日志输出目标(
audit_log_file)建议设为独立磁盘路径,避免与 error log / slow log 争抢 I/O;同时配audit_log_rotate_on_size防止单文件过大
audit_log 输出里怎么快速定位 GRANT/REVOKE 操作?
audit_log 默认是 JSON 格式,每行一条记录。权限变更操作对应 "command_class": "grant" 或 "command_class": "revoke",但字段嵌套深、可读性差。推荐用以下方式过滤:
zgrep '"command_class":"grant"\|'"command_class":"revoke"' /var/lib/mysql/audit.log.* | jq -r 'select(.status == 0) | "\(.timestamp) \(.user)@\(.host) \(.query)"'
注意点:
-
.status == 0表示执行成功,避免抓到语法错误的失败语句 -
.user和.host是实际执行者,不是被授权用户;被授权对象在.query字段里,需正则提取 - 如果启用了
audit_log_exclude_accounts,确保 DBA 账户没被排除,否则漏审
audit_log 无法覆盖的场景有哪些?
插件机制决定它有天然盲区:
- 通过
UPDATE mysql.user或直接修改系统表绕过权限语句的操作——audit_log 不触发,必须配合定期校验mysql.user、mysql.db、mysql.tables_priv等表的Update_time或 checksum - 使用
--init-command启动客户端并自动执行 GRANT 的情况——audit_log 记录为普通查询,command_class可能是"query"而非"grant" - MySQL 5.6 及更早版本不支持该插件,只能靠慢日志 + 正则匹配(不可靠)或升级
最易被忽略的是:audit_log 不记录权限生效时间(即何时被其他会话感知),只记录语句执行瞬间。如果应用缓存了权限(如未执行 FLUSH PRIVILEGES),审计日志和实际效果之间存在延迟偏差。










