MySQL 8.0+ 审计日志真正可用必须设 log_statement = 'ALL',配合 log_output = 'TABLE'(写入 mysql.general_log,需转为 InnoDB 引擎)或 'FILE'(输出 JSON 格式),且须确认 log_output 非 'NONE'、log_statement 已生效,否则无法完整捕获真实 SQL 结构。

必须直接解析 mysql.general_log 表或 JSON 审计日志里的 sql_text 字段,其他日志源(如 Nginx access.log、应用层 debug 日志)无法还原真实 SQL 结构,基本无效。
MySQL 8.0+ 审计日志怎么开才真正可用
log_statement = 'ALL' 是唯一推荐方式,general_log 已被弃用作安全审计——它默认用 CSV 引擎、无索引、仅对新连接生效,线上开等于自造延迟。必须配合:log_output = 'TABLE'(写入 mysql.general_log)或 log_output = 'FILE'(输出 JSON 格式文件)。若选 TABLE,记得事后改引擎:ALTER TABLE mysql.general_log ENGINE = InnoDB;,否则查大日志极慢。
常见错误现象:
- 开了
general_log却查不到语句:其实是log_output还是'NONE',得执行SELECT @@global.log_output;确认 - 日志文件没生成:检查
log_error里有没有Failed to open log file,常因权限或路径不存在 - 只看到 CONNECT/Quit,看不到 QUERY:说明
log_statement没设成'ALL',而general_log不记录参数化查询的值
从 sql_text 里识别高置信度注入载荷
别扫 “union”“select” 这类关键词——业务 SQL 本身就有大量合法 UNION SELECT。真正该匹配的是「语法异常 + 上下文可疑」组合:
-
OR\s+1\s*=\s*1出现在单引号闭合后,例如id='1' OR 1=1或name='a'' OR ''x''=''x' -
UPDATEXML\(|EXTRACTVALUE\(|FLOOR\(RAND\(0\),括号间允许空格或换行,正则要加\s* -
UNION\s+ALL\s+SELECT.*?FROM.*?(information_schema|mysql\.user),注意点号要转义 - 语句末尾带
;--或;#,且前面不是CREATE PROCEDURE等合法多语句场景
性能影响:在 Logstash 中先用 json 插件提取 sql_text 字段,再在这字段上跑轻量正则,比全局 grok 快 3 倍以上,漏报率下降 70%。
Kibana 告警必须带上下文字段,否则没法研判
光标红一条 sql_text: "SELECT * FROM users WHERE id = 1 OR 1=1" 没用。至少要带上:user_host(谁连的)、command(是 Query 还是 Connect)、time(时间戳)、argument(原始日志行)。否则你分不清这是运维脚本、定时任务,还是攻击者在扫库。
容易踩的坑:
- 没过滤系统账号(如
root@localhost)或内部中间件账号,导致告警被刷屏 - 把
SELECT * FROM mysql.user当成攻击,其实可能是 DBA 在查权限——得结合user_host和频率判断 - 忽略 MySQL 8.0+ 的 JSON 日志格式变化:字段名是
sql_text,不是argument,硬套旧 grok 模式会全丢
最复杂也最容易被忽略的一点:MySQL 审计日志不记录参数化查询中 ? 对应的真实值,WHERE id = ? 就是字面量。所以必须确保应用层没滥用预编译——如果所有语句都走 PREPARE/EXECUTE,那审计日志里就全是问号,根本没法检测。真要防注入,得靠代码审计找拼接点,而不是指望日志抓到 payload。


















