SQL注入本质是用户输入未经隔离直接拼入SQL字符串,防御核心是杜绝拼接、强制参数化,并对动态表名等无法参数化的场景实施白名单校验。

直接搜SQL动词比找数据库函数更有效——绝大多数注入点藏在拼接逻辑里,而不是执行位置。
全局搜索SQL关键词,过滤干扰项
在IDE或命令行中大小写不敏感地搜索 SELECT、INSERT、UPDATE、DELETE、UNION 等关键字,能快速定位所有硬编码SQL字符串。但别只靠关键词匹配,容易被注释、变量名(如 $selectSql)或日志语句带偏。
- 用正则提升精度:比如
(select|insert|update|delete|union)\s+from,强调空格与from的组合,大幅减少误报 - 排除明显非SQL场景:以
//或/*开头的注释行、文件路径中的select、类名含UpdateHandler等 - 额外关注
${}占位符和<script></script>标签——MyBatis风格的${}在PHP模板中若被自行实现,等同于字符串拼接
盯紧所有拼接操作和用户输入来源
SQL注入的本质是用户输入进了SQL字符串。只要没走参数化,拼接即高危。
- PHP中:
.连接、sprintf()、str_replace()后再拼接、甚至implode()构造IN子句,都属于风险操作 - 别只盯着
$_GET和$_POST:污染源还包括$_COOKIE、$_SERVER['HTTP_USER_AGENT']、$_SERVER['HTTP_REFERER']、file_get_contents()读取的文件内容,甚至数据库查出后再拼进新SQL的字段(二次注入) - 看到任何外部输入变量出现在SQL附近,立刻顺藤摸瓜:是否做过类型转换?是否经过预编译?有没有封装层把拼接逻辑藏得更深?
警惕“看似安全”的预编译绕过点
用了 PDO::prepare() 或 mysqli_prepare() 就绝对安全?不一定。风险常卡在边界上。
立即学习“PHP免费学习笔记(深入)”;
- 前半段参数化,后半段仍拼接:例如
"SELECT * FROM user WHERE status = ? AND type IN (" . $typeList . ")",IN子句未参数化,照样可注入 - 自定义过滤函数失效:比如调用
IFilter::act($input, 'string')做了转义,但后续拼接时漏掉引号(WHERE id = $id而非WHERE id = '$id'),转义完全失效 - 动态表名/字段名无法参数化:若SQL中拼接了用户控制的表名(如
"SELECT * FROM {$table}"),必须用白名单校验,不能依赖过滤或转义
验证与闭环:从静态到动态确认
找到可疑点后,不能只看代码结构,要结合实际数据流验证。
- 追踪调用链:从入口(如
$_POST['id'])一路跟到SQL拼接处,确认中间无净化、无类型强制、无可信上下文覆盖 - 检查过滤逻辑是否真正生效:有些项目有统一过滤类,但可能被绕过(如传入数组触发递归处理异常)、或仅对部分参数启用
- 有条件可配合数据库监控:开启MySQL general_log,复现请求,观察真实执行的SQL是否含用户输入原始值



















