thinkphp中where()传数组仍可能被sql注入,因用户输入的键名未过滤会直接拼入sql;字段名、排序参数等结构部分必须白名单校验,值才通过预处理绑定安全。

ThinkPHP里用where()传数组为什么还会被注入?
因为数组键名若来自用户输入且未过滤,ThinkPHP会直接拼进SQL——比如$map[$_GET['field']] = $_GET['value'],攻击者传field=id%20or%201=1就能绕过。构造器本身不自动校验键名合法性,只对值做参数绑定。
实操建议:
- 禁止把用户输入直接当数组键使用;必须白名单校验字段名,例如:
in_array($key, ['name', 'status', 'created_at']) - 改用链式
where()明确指定字段:->where('name', 'like', '%'.$keyword.'%'),此时值自动转为预处理参数 - 如需动态字段,先映射再拼接:
$fieldMap = ['u' => 'username', 'e' => 'email']; $realField = $fieldMap[$type] ?? 'username';
手写whereRaw()或query()->execute()时怎么保安全?
这两个接口跳过参数绑定机制,值直接拼入SQL,是最常见的注入入口。哪怕只拼一个id,也不该写whereRaw("status = ".$_GET['s'])。
实操建议:
-
whereRaw()必须配合占位符:whereRaw('age > ? AND type IN (?)', [$minAge, $types]),第二个参数会自动展开并绑定 - 执行原生SQL优先用
query()->execute()的参数形式:$db->execute('UPDATE user SET status=? WHERE id=?', [$newStatus, $id]) - 绝对避免字符串拼接+
echo调试——调试时用getLastSql()看最终语句,确认问号已被替换且无意外拼接
模型save()和update()批量赋值怎么防污染?
如果直接$user->save($_POST),攻击者可在表单里加个is_admin=1字段,只要数据库字段存在、模型没设protected $readonly或protected $allowField,就会被写入。
实操建议:
- 永远显式声明可写字段:
protected $allowField = ['name', 'email', 'avatar']; - 不用
save($_POST),改用data($_POST)->allowField(true)->save(),否则$allowField不生效 - 敏感字段(如
status、role_id)必须在业务层硬编码赋值,不依赖任何用户输入
分页order()参数被篡改导致报错或信息泄露
常见写法order($_GET['sort']),攻击者传sort=id; DROP TABLE user;虽不会执行多语句,但可能触发Unknown column报错,暴露字段名;更危险的是sort=updatetime desc, (SELECT password FROM admin)这类子查询注入。
实操建议:
- 白名单限制排序字段和方向:
$orders = ['id'=>'id', 'name'=>'name', 'ctime'=>'create_time']; $dirs = ['asc','desc']; - 组合时严格校验:
if (isset($orders[$f]) && in_array($d, $dirs)) { $list->order($orders[$f], $d); } - 不要依赖
htmlspecialchars()或strip_tags()——这些对SQL语法无效,只防XSS
where()的键名污染和order()的任意字段拼接——这两处不像函数调用那么显眼,但恰恰是线上被攻破的高频路径。php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











