直接查日志中WHERE、UNION、SELECT等关键字及绕过变体,重点盯remote_addr、request_uri、http_user_agent、status、time_local字段,并结合慢查询日志、WAF日志和应用框架SQL日志交叉验证。

怎么看日志里有没有SQL注入痕迹
直接查 WHERE、UNION、SELECT、FROM、OR '1'='1、--、#、/* 这类字符串,尤其是出现在 URL 参数、POST body 或请求头中。真实攻击流量里,这些词往往夹在正常参数里,比如 ?id=1' AND SLEEP(5)-- 或 ?q=admin' OR 1=1#。注意大小写混用、URL 编码(如 %27 代替单引号)、空格被替换成 /**/ 等绕过手法。
哪些日志字段最值得盯
重点关注以下几项,缺一不可:
-
remote_addr(客户端 IP):同一 IP 在短时间内高频出现可疑参数,基本可锁定攻击源 -
request_uri或args:完整 URL 和查询参数,是判断 payload 类型(布尔盲注、报错注入、堆叠注入)的核心依据 -
http_user_agent:出现python-requests、sqlmap、curl、nikto等特征 UA,基本坐实自动化扫描 -
status和body_bytes_sent:布尔盲注常伴随大量 200 响应但响应体长度有规律性差异(如真/假返回页大小不同);时间盲注则可能看到 504 或超长响应耗时 -
time_local:用于确认是否为短时密集请求(如 1 秒内发 20 条带IF(SUBSTRING(...的请求)
为什么单纯 grep 会漏掉关键线索
因为攻击者早就不直接写 SELECT * FROM users 了。常见混淆手段包括:
- 用函数嵌套绕过关键词检测:
CONCAT(0x7e,(SELECT USER()),0x7e)、EXTRACTVALUE(1, CONCAT(0x7e, DATABASE())) - 用注释符拆分关键字:
SEL/**/ECT、UNI/**/ON - 用十六进制或 char() 绕过字符串过滤:
0x73656C656374(select 的 hex)、CHAR(115,101,108,101,99,116) - 利用数据库特性做逻辑变形:
' AND (SELECT COUNT(*) FROM information_schema.tables) > 10不触发报错也不回显,但能探知库结构
所以不能只依赖固定字符串匹配,得结合行为模式(如某 IP 对同一接口反复提交不同长度的 IF(...,SLEEP(3),1))来交叉验证。
实战中容易忽略的细节
很多团队查完 access.log 就停了,但真正关键的证据常藏在别处:
- 数据库慢查询日志(如 MySQL 的
slow_query_log)里可能记录了实际执行的恶意语句,比 Web 日志更“干净”——没有编码干扰,直接看到SELECT ... FROM users WHERE id = '1' OR '1'='1' - WAF 或云防火墙日志里的
rule_id字段,如果命中了 SQLi 规则(如942100、942110),说明规则已识别,但应用层没记录原始请求,此时必须关联 WAF 日志和 Nginx 日志的 timestamp + IP + URI - 应用框架自身的 debug 日志(如 Django 的
django.db.backends、Laravel 的 query log),若开启,能直接看到拼接后的完整 SQL,是定位漏洞点的黄金证据
最麻烦的情况是二阶注入:攻击者先把恶意 payload 存进数据库(比如评论区输入 admin'--),等后台某处代码读取并拼接执行时才触发。这种在 Web 日志里几乎不露痕迹,只能靠数据库日志 + 应用层 SQL 打印联合排查。

















