<p>SQL注入日志需从数据库审计日志识别,如MySQL的sql_text:"SELECT * FROM users WHERE id = 1 OR 1=1"或pg_audit中引号包裹的恶意语句,而非Web访问日志中的URL编码参数。</p>

SQL注入日志长什么样?先认出它再谈分析
ELK(Elasticsearch + Logstash + Kibana)本身不识别SQL注入,它只存你喂给它的原始日志。真正能暴露注入行为的,是数据库审计日志里的异常模式:SELECT * FROM users WHERE id = '1' OR '1'='1'、UNION SELECT username, password FROM admins 这类明显违背业务逻辑的语句。如果你用的是 MySQL 8.0+,开启 log_statement = 'ALL' 或审计插件;PostgreSQL 则依赖 pg_audit 扩展。没开审计,Logstash 再努力也捞不到有效载荷。
- 常见错误:直接解析应用层 access.log,里面只有
/api/user?id=1%27%20OR%201%3D1这种编码串,无法还原真实 SQL,误报率极高 - 关键区别:数据库审计日志记录的是「执行前解析后的语句」,而 Web 日志只记录「请求参数原始值」
- 性能影响:MySQL 审计插件在高并发下可能增加 5–15% 延迟,建议仅对敏感库启用
Logstash 怎么从审计日志里抽 SQL 片段?别硬写正则
审计日志格式千差万别(MySQL 审计日志是 JSON,pg_audit 是纯文本,Oracle 审计又是另一种),硬写通用正则只会漏掉 /* */ 注释包裹的 payload 或换行拆分的多行语句。优先用 Logstash 自带的 json 或 dissect 插件解析结构化字段,再用 grok 补充非结构部分。
- MySQL 审计日志示例(JSON):
{"timestamp":"1712345678","command_class":"select","sql_text":"SELECT * FROM accounts WHERE id = 1 OR 1=1"}→ 直接用json插件解析sql_text字段 - pg_audit 文本日志:
AUDIT: SESSION,1,1,READ,SELECT,public.users,"SELECT * FROM users WHERE name = 'admin''; DROP TABLE users; -- ',<not logged></not>→ 用dissect提取引号内内容,比grok更快更稳 - 坑点:别在
grok里写%{SQL}这种自定义模式——没人统一定义,维护时你会想删库跑路
Kibana 里怎么快速标出可疑 SQL?靠规则不是靠肉眼
人工翻 Kibana Discover 页面找 UNION SELECT 或 EXEC xp_cmdshell 效率极低。必须用 Elasticsearch 的 query_string 查询 + Kibana 的 saved search + alerting 规则联动。重点不是“匹配关键词”,而是“匹配非常规组合”——比如 WHERE 后面紧跟 OR 1=1,或 SELECT 语句里出现非业务表名。
- 实用查询示例:
sql_text: ("UNION" AND "SELECT") OR (sql_text: "OR 1=1" AND sql_text: "WHERE")—— 注意加括号控制优先级 - 避免误报:排除已知白名单,比如运维脚本固定用
SELECT COUNT(*) FROM logs WHERE created_at > '2024-01-01',加NOT (sql_text: "COUNT(*)" AND sql_text: "logs") - 兼容性注意:Elasticsearch 8.x 默认禁用
query_string中的通配符前缀(如*admin),要用wildcard字段类型替代
为什么查到了也拦不住?ELK 本质是事后分析工具
ELK 不是 WAF,它不拦截流量,只帮你确认“攻击是否成功”。你看到 INSERT INTO users VALUES ('hacker', 'xxx') 出现在审计日志里,说明数据库已经执行了——这时候删日志也晚了。真正要做的,是在 Logstash 解析阶段就触发告警(发 Slack / 邮件),同时把 client_host 和 user_name 推给防火墙或数据库层做临时封禁。
- 典型断层:Logstash 解析出恶意 SQL 后,没配置
http输出插件回调封禁 API,告警只停留在邮件里 - 权限陷阱:MySQL 审计日志默认只记录用户执行语句,不记录客户端 IP(除非开启
log_slow_extra = ON并配合 slow log),client_host字段可能为空 - 最易忽略的一点:审计日志时间戳和 Logstash 接收时间可能差几秒,做实时告警时得用
@timestamp字段而非系统时间

















