mysql默认不记录登录或查询行为,必须启用audit_log插件并正确配置plugin_load_add、audit_log=force_plus_permanent、audit_log_format=json、audit_log_policy=commands(或all)、audit_log_file等参数,重启mysqld后才可审计非法访问。

audit_log插件必须启用且配置正确,否则日志里根本看不到非法访问痕迹
MySQL 默认不记录任何登录或查询行为,audit_log 插件不装、不配、不重启,就等于没开。社区版 8.0.19+ 才内置支持,5.7 只有企业版能用;低于这个版本的社区版(比如 5.7.26)执行 INSTALL PLUGIN audit_log SONAME 'audit_log.so' 会报错 “Plugin 'audit_log' is not loaded”,不是权限问题,是压根不带这个二进制文件。
确认是否可用,先跑:SELECT VERSION(); 和 SHOW PLUGINS LIKE 'audit_log';。如果输出为空或 Status 是 DISABLED,后续所有分析都无从谈起。
- 插件加载后必须写入 my.cnf 的
[mysqld]段,并设plugin_load_add = audit_log.so+audit_log = FORCE_PLUS_PERMANENT,仅靠 SQL 命令安装,重启即失效 -
audit_log_format = JSON必须显式设置,旧的NEWLINE格式字段缺失严重(比如没有status码),无法区分成功/失败访问 - 改完配置一定要
systemctl restart mysqld,FLUSH PRIVILEGES或SERVICE mysql reload都不生效
audit_log_policy=LOGINS 不够用,非法数据访问得靠 COMMANDS 或 ALL
只设 audit_log_policy = LOGINS,日志里只有连接和断开事件,查不到谁 SELECT 了敏感表、谁导出了用户表、谁用 LIMIT 0,1000 扫描了整张订单表——这些全属于 command 类型事件,不在 LOGINS 范围内。
真正要捕获非法数据访问,至少得设 audit_log_policy = COMMANDS。它会记录所有非管理类语句(SELECT、INSERT、UPDATE、DELETE、EXECUTE),但不包括 GRANT、DROP USER 这类权限操作(那些归在 access 类型下,需 ALL 才覆盖)。
-
audit_log_policy = ALL虽全面,但 IO 压力陡增,尤其高并发 SELECT 场景,磁盘写满风险极高,不建议长期开启 - 若只想盯住几张敏感表,可用
audit_log_include_tables(MySQL 8.0.22+ 支持),例如设为audit_log_include_tables = 'user_info,payment_log' - 排除监控账号减少噪音:
audit_log_exclude_accounts = 'monitor,backup',注意格式是逗号分隔、无空格
从 JSON 日志里快速定位可疑 SELECT 行为
日志默认写进 error_log,容易被淹没。建议单独设 audit_log_file = /var/lib/mysql/audit.json,并确保 mysqld 进程用户(通常是 mysql)对该路径有写权限——sudo -u mysql touch /var/lib/mysql/audit.json 能成功才靠谱。
JSON 格式每行一条记录,关键字段包括:timestamp、user、host、command_class(值为 select 表示数据访问)、sqltext(SQL 原文)、status(0=成功,非0=失败,如 1045 是密码错误)。
- 查所有远程 SELECT:
grep '"command_class":"select"' /var/lib/mysql/audit.json | grep -v '"host":"localhost"' - 查访问了 user 表但不是 DBA 用户:
jq -r 'select(.command_class == "select" and .sqltext | contains("user")) | "\(.timestamp) \(.user)@\(.host) \(.sqltext)"' /var/lib/mysql/audit.json - 查失败的敏感操作:
grep '"status":1045\|'"status":1142' /var/lib/mysql/audit.json(1045=登录失败,1142=SELECT 权限拒绝,说明有人在试探权限边界)
audit_log 不记录执行者真实 IP?代理和连接池会让 host 字段失真
host 字段记录的是 MySQL 看到的客户端地址,如果前端有 HAProxy、Nginx 或应用层连接池,这里显示的往往是中间层 IP(如 10.0.1.100),而非终端用户真实出口 IP。这不是配置错误,是网络架构决定的。
想补全来源信息,不能只依赖 audit_log:
- 在应用侧记录请求上下文(如 HTTP Header 中的 X-Real-IP),并写入业务日志,与 audit_log 时间戳对齐做关联分析
- 若用的是阿里云 DAS 或腾讯云 DBbrain,它们的 SQL 洞察功能可自动解析代理链路,还原真实 client_ip,但 audit_log 本身做不到
- 临时排查时,立刻连上库执行:
SELECT ID, USER, HOST, INFO FROM information_schema.PROCESSLIST WHERE COMMAND = 'Query' AND INFO LIKE '%SELECT%';,看当前活跃会话的HOST是否更准确
audit_log 的价值在于“不可抵赖”和“事后回溯”,但它不是网络层抓包工具;把 host 当作绝对真实 IP,是排查初期最常踩的坑。











