日志无法溯源到具体攻击者个人身份,仅能定位请求IP、时间、SQL片段、执行账户及受影响表;general_log不记录HTTP头和真实客户端IP,且明文存储SQL存在密码泄露风险。

SQL注入攻击本身无法通过日志“溯源到具体攻击者IP以外的个人身份”,系统日志和数据库日志最多能帮你定位到请求来源IP、时间、原始SQL语句片段、执行账户、受影响表,以及是否绕过WAF或中间件——但别指望日志里写着“张三用burpsuite在2024-05-22 14:32:17发起了union select null,version(),null”。
查 general_log 能看到什么,又看不到什么
MySQL开启general_log后,会记录所有到达MySQL服务端的语句(含连接、断开、查询),但它不记录HTTP头、User-Agent、Referer,也不记录请求来自哪个反向代理的真实客户端IP——如果前端有Nginx或CDN,general_log里显示的host通常是内网地址(如10.0.1.5)或localhost。
- 开启方式:
SET GLOBAL general_log = ON;,日志路径由general_log_file决定,默认可能写入/var/lib/mysql/hostname.log - 关键字段只有:
Time、User@Host(注意:这里的Host是MySQL解析出的连接来源,非HTTP层X-Forwarded-For) - 危险点:
general_log默认记录明文SQL,若含INSERT INTO users VALUES ('admin', 'pwd123'),密码就直接裸奔了——生产环境切勿长期开启 - 替代方案:用
slow_query_log+long_query_time=0可捕获所有慢查询,但依然不解决来源IP失真问题
系统日志(如Nginx access_log)和数据库日志必须交叉比对
单看任一日志都容易误判。比如Nginx日志里有一条GET /api/user?id=1%27%20UNION%20SELECT%201,2,3--,但MySQL日志里没对应记录——说明请求根本没进MySQL,可能被WAF拦截、路由失败,或应用层做了预校验并返回400。
- Nginx需开启
$request_body记录(需log_format自定义),否则POST型注入(如login?username=admin'--&password=123)在access_log里只显示URL参数,丢掉关键payload - 数据库日志中的
User@Host若为app@10.0.2.10,就要去查该IP对应的业务服务日志,确认它收到的原始HTTP请求是什么 - 时间戳必须用同一时区、同一NTP源同步,否则5ms级误差会导致关联失败——尤其高并发下,一条注入请求可能触发多条SQL,顺序错乱
information_schema.PROCESSLIST 和审计插件不是实时溯源工具
SHOW PROCESSLIST或查information_schema.PROCESSLIST只能看到当前正在运行的连接和语句,攻击一旦结束就清空;它不能回溯已执行完的注入行为。想靠它抓“正在执行union select”的瞬间,基本靠运气。
- MySQL企业版的
Audit Log Plugin或Percona Server的audit_log可记录更细粒度事件(含返回行数、错误码),但默认不记录完整SQL参数值,且日志体积爆炸,需配合audit_log_policy = ALL和audit_log_exclude_accounts谨慎配置 - 开源替代如
mysql-sniffer(基于tcpdump解析MySQL协议包)能拿到原始payload,但依赖root权限抓包、不兼容SSL连接、无法区分复用连接里的多条语句 - 真正有效的做法:在应用ORM层(如MyBatis的
Interceptor、Django的DatabaseWrapper)埋点,记录sql、params、request_id、remote_addr,再与APM链路ID打通
日志能告诉你的上限,就是“某个IP在某时刻用某个账号执行了一条可疑SQL”,剩下的——比如这个IP是代理还是肉鸡、payload是否成功读取了敏感数据、有没有后续横向移动——得靠网络流量分析、内存取证或EDR联动。别把日志当万能胶水,它只是拼图里最基础、也最容易被伪造的一块。

















