SQL注入检测需基于SQL解析层或语义分析,而非日志关键词匹配;应记录未参数化拼接的上下文(如调用栈、模板化SQL),并配套白名单与误报反馈机制。

SQL注入检测不能靠日志关键词匹配
只在日志里搜 ' OR 1=1 或 UNION SELECT 这类字符串,漏报率极高。攻击者用 URL 编码、大小写混用、注释符绕过、空格替换成 %09(制表符)等手法,让原始 payload 看起来完全不像“典型注入”。真正有效的检测必须落在 SQL 解析层或执行前的语义分析上。
- 应用层做简单正则拦截,只能拦住脚本小子,对稍有经验的攻击者无效
- WAF 规则若只依赖特征串,容易被
SEL/**/ECT或select%091绕过 - 真正可信的判断点:数据库驱动是否把用户输入当字面量处理?有没有走预编译?
记录不安全请求的前提是识别出“未参数化的拼接”
不是所有含单引号的请求都该记——关键看它是否出现在 query 字符串拼接中且未被转义或绑定。比如 Python 的 cursor.execute("SELECT * FROM users WHERE id = " + user_id) 是高危;而 cursor.execute("SELECT * FROM users WHERE id = %s", [user_id]) 即使 user_id 是 "1 OR 1=1",也不会触发注入,也不该进“不安全请求”日志。
- Java 中检查是否调用了
Statement.execute()而非PreparedStatement.execute() - PHP 中注意
mysql_query()(已废弃)、mysqli_query()直接拼接,而非mysqli_prepare() - Node.js 的
mysql2库里,connection.query("SELECT ... " + input)才危险,connection.execute()自动参数化则安全
记录内容必须包含可追溯的上下文,否则日志等于没记
光记下 SELECT * FROM users WHERE name = 'admin'-- 没用。没有调用栈、没有 HTTP Referer、没有对应 controller/action 名、没有绑定参数的实际值,你根本没法定位是哪个业务接口、哪行代码、哪个开发写的这个拼接逻辑。
- 至少记录:
request_id、http_method、path、remote_addr、user_agent、stack_trace(到 SQL 执行那一行) - 不要记录完整原始 SQL(可能含密码、手机号),而是记录模板化后的形式,例如把数字/字符串替换成
?,保留结构:SELECT * FROM orders WHERE uid = ? AND status IN (?, ?) - 避免同步写日志阻塞主流程,用异步队列或内存缓冲+定期刷盘,否则高并发下直接拖垮服务
别把 sql_mode=STRICT_TRANS_TABLES 当防护手段
MySQL 开启严格模式只是让某些非法输入报错,不阻止注入本身。比如 id=1; DROP TABLE users; 在允许多语句的配置下(mysqli.multi_query),严格模式完全不生效。而且 PostgreSQL / SQLite 根本没有等效机制。
-
sql_mode影响的是数据校验,不是查询解析安全性 - PostgreSQL 的
standard_conforming_strings=on只影响反斜杠处理,和注入无关 - 真正起作用的是:是否使用占位符、是否禁用多语句执行、是否关闭动态表名/列名拼接
最常被忽略的一点:日志里记下的“不安全请求”,90% 都来自内部测试或历史遗留脚本,不是真实攻击。得配套建立白名单机制和误报反馈通道,不然运维每天收到几百条告警,最后只会把整个日志开关关掉。










