ThinkPHP的where()方法不防SQL注入因其本身不自动转义,安全性取决于参数类型:传数组或键值对触发预处理机制,传字符串字面量则直接拼接导致漏洞;field()、table()、order()等方法更需白名单校验。

ThinkPHP的where()方法为什么有时不防SQL注入
因为where()本身不自动转义,它只是拼接条件的入口,是否安全取决于你传入的参数类型和写法。传字符串字面量(比如"id = $_GET['id']")等于直接开后门;传数组或键值对(如['id' => $id])才触发预处理机制。
- 用
where(['name' => $input])→ 安全,走PDO预处理 - 用
where("name = '$input'")→ 危险,字符串拼接,$input里塞' OR 1=1 --就中招 - 用
where('name', 'like', $input)→ 安全,框架会加%并绑定参数 - 用
whereRaw("name = '{$input}'")→ 危险,绕过所有过滤,慎用
如何快速验证当前SQL是否被参数化
打开app.php里的'show_sql' => true,再在日志或页面底部看实际执行的SQL。如果看到?占位符(如SELECT * FROM user WHERE id = ?),说明参数已绑定;如果看到原始变量值(如WHERE name = 'admin' OR 1=1'),说明没走预处理。
- 检查
think\db\Connection::query()调用链,重点看$this->getRealSql()返回值 - 调试时临时加
dump($this->getLastSql()),但注意它返回的是格式化后的SQL,可能掩盖?细节 - 真要确认绑定行为,得看
PDOStatement::bindValue()是否被调用,可用xdebug断点到think\db\PDOConnection::execute()
input()函数不能当“万能过滤器”用
input()默认只做类型转换和空格裁剪,不转义SQL特殊字符。它返回的仍是原始用户输入,除非你显式指定过滤规则,比如input('get.id', '', 'htmlspecialchars'),但这只防XSS,不防SQL注入。
-
input('post.name')→ 等价于$_POST['name'],零过滤 -
input('post.id/d')→ 转成整型,可防部分注入,但遇到id[]=1&id[]=2会返回数组,后续若不当处理仍可能出问题 -
input('post.sql', '', 'addslashes')→ 错误思路,magic_quotes已被废弃,且addslashes无法覆盖所有边界场景(如宽字节注入) - 真正该做的是:把
input()结果当作普通变量,再交给where()等安全方法处理,而不是指望它“洗干净”再拼SQL
自定义查询里最容易漏掉的两个坑
一是field()和table()支持传表达式,二是order()允许直接传字符串。这俩地方没人帮你参数化,一旦拼接用户输入,风险比where()还高。
立即学习“PHP免费学习笔记(深入)”;
-
->field(input('field'))→ 若用户传id, password as p from users,直接导致列名/表名注入 -
->table(input('table_name'))→ 传user UNION SELECT * FROM admin?框架照单全收 -
->order(input('sort') . ' ' . input('order'))→sort=id; order=ASC, (SELECT password FROM admin)就能拖库 - 对策:白名单校验。比如
in_array($field, ['id', 'name', 'status']),别试图“过滤”字段名
复杂查询里,参数化只覆盖WHERE、HAVING、LIMIT这些值位置,字段名、表名、排序方向、函数名这些语法结构,必须靠逻辑层硬校验——这点很多人调试时根本没想到,直到上线被扫出漏洞。


















