应从MySQL慢日志中按SQL模板聚合分析执行时间方差与行数抖动,设long_query_time为0.1,用pt-query-digest聚焦Query_time:max/avg>10且Rows_sent标准差远超均值的条目,盯住执行计划突变(如type从ref变ALL),而非依赖general_log或简单关键字匹配。

SQL注入在日志里几乎从不自报家门,你得从执行行为的“不对劲”里揪出它——不是看有没有MySQLSyntaxErrorException,而是看同一条SQL模板下,不同参数引发的执行时间、扫描行数、执行计划是否剧烈抖动。
怎么从 MySQL 慢日志里快速定位可疑 SQL 模板
慢日志本身不标注入,但能暴露「同一语句、不同参数、天差地别的性能」。关键不是单条慢 SQL,而是聚合后发现耗时方差大、返回行数飘忽:
-
long_query_time必须设为0.1(不是默认1),否则SLEEP(5)类盲注试探直接被过滤 - 用
pt-query-digest --group-by fingerprint --order-by Query_time:max聚合,重点关注Query_time:max / Query_time:avg > 10且Rows_sent标准差远大于均值的条目 - 别信
log_queries_not_using_indexes=ON—— 它只报“没走索引”,和注入无关,反而污染结果
为什么不能只查 general_log 表里的 UNION SELECT
线上环境基本不开 general_log,开了也撑不住:日志量爆炸、mysql.general_log 表默认无索引、CSV 存储格式查起来像刮痧。更致命的是,攻击者早把关键字拆开、编码、大小写混用:
-
UNION%20SELECT、unIoN/**/selECT、%75%6e%69%6f%6e%20%73%65%6c%65%63%74都不会命中LIKE '%union%select%' - 真正该盯的是执行行为:比如一个本该走
idx_user_id的查询,在传入' OR 1=1后突然全表扫描,EXPLAIN结果里type从ref变成ALL - MySQL 5.7+ 的直方图统计、
optimizer_switch开关、甚至临时表引擎切换,都可能让同一条 SQL 在不同参数下走完全不同的执行路径
PostgreSQL 和 SQL Server 日志分析要注意什么
各数据库审计机制差异大,硬套 MySQL 方法会漏掉关键线索:
- PostgreSQL 的
pg_stat_statements要查max_exec_time / mean_exec_time > 15的queryid,而不是扫pg_log文件里的错误文本 - SQL Server 的审计事件类型是
BATCH STARTED,不是EXECUTE—— 攻击者用分号拼多条语句时,只有一条BATCH记录,后面所有子语句都不会单独记审计日志 - pg_audit 不是默认模块,必须先
CREATE EXTENSION pg_audit,再配pgaudit.log = 'read, write, role, ddl',否则压根不记录 DML/DQL
最常被忽略的一点:连接池阻塞会让慢查询的毛刺传导到后续请求,导致你看到的“慢接口”其实是被前面那条注入语句拖垮的——所以必须按 SQL 模板(含占位符位置)单独统计 P95 延迟,不能按 URL 或用户 ID 聚合。

















