必须传第二个参数绑定数组的Raw方法有:selectRaw()、whereRaw()、orderByRaw()、havingRaw()、groupByRaw();未绑定时用户输入直入SQL引发注入,字段名在orderByRaw()等中不可参数化,须白名单校验。

哪些 Raw 方法必须传第二个参数绑定数组
所有名字带 Raw() 的 Eloquent 方法,只要接受用户输入参与 SQL 结构拼接,就必须传第二个参数——否则就是裸奔式注入。明确必须配绑定的有:selectRaw()、whereRaw()、orderByRaw()、havingRaw()、groupByRaw()。
常见错误是只写第一个 SQL 字符串,比如:
$query->whereRaw("name like '%$keyword%'")这等于把 $keyword 直接插进 SQL 里解析,攻击者传 ' OR 1=1 -- 就能绕过条件;传 1' AND SLEEP(3) OR '1'='1 还能测延时盲注。
正确写法必须用问号占位 + 数组绑定:
$query->whereRaw("name like ?", ["%{$keyword}%"])$query->whereRaw("name like concat('%', ?, '%')", [$keyword])- 注意:不能写成
concat('%', '$keyword', '%')——PHP 变量插值一出现,参数化就失效了
orderByRaw() 里的字段名为什么不能参数化
SQL 标准不允许对排序字段(如 id、created_at)做参数化绑定,数据库预编译阶段不接受字段名作为“值”传入。所以即使你写 orderByRaw('?', [$sortField]),PDO 会直接报错或忽略。
必须靠白名单硬校验:
- 先定义允许排序的字段列表:
$allowedSorts = ['id', 'name', 'created_at', 'updated_at'] - 再严格判断:
if (!in_array($sortField, $allowedSorts, true)) { abort(400); } - 最后才执行:
$query->orderByRaw($sortField . ' ' . ($sortDir === 'desc' ? 'DESC' : 'ASC'))
别信“过滤掉单引号就行”,字段名控制权一旦放开,攻击者可注入 id, (SELECT password FROM users LIMIT 1) 这类子查询。
DB::select() 和 DB::statement() 是最常被忽略的注入点
这些方法完全绕过 Eloquent 的绑定机制,直接走 PDO 原生执行。哪怕只有一处写了:
DB::select("SELECT * FROM users WHERE name = '$name'")就等于在防火墙上凿了个洞。这类写法在搜索、导出、统计类逻辑里高频出现,尤其喜欢用字符串拼接加 $_GET 或 request()->input()。
修复方式只有两种:
- 改用
DB::table('users')->where('name', $name)->get()等安全链式调用 - 若必须原生 SQL,强制改用预处理语法:
DB::select("SELECT * FROM users WHERE name = ?", [$name])
注意:DB::raw() 本身不执行,只是包装字符串,不构成风险;但一旦放进 DB::select() 或 DB::insert() 这类执行函数里,且没配绑定,就立即变高危。
when() 链式调用中嵌套 whereRaw() 容易漏绑
开发者常以为 when() 自动继承安全上下文,其实不然。下面这段代码看着干净,实则危险:
$query->when($keyword, function ($q) use ($keyword) {
return $q->whereRaw("name like '%$keyword%'");
})因为 whereRaw() 在闭包里单独调用,没传第二个参数,$keyword 仍直入 SQL。
正确写法要显式绑定:
return $q->whereRaw("name like ?", ["%{$keyword}%"])- 或者更稳妥地提前校验:
if (strlen($keyword) > 100) { abort(400); }
这种嵌套场景最难排查——静态扫描工具通常识别不出闭包内的 whereRaw() 调用是否漏绑,必须人工逐行 review。
真正麻烦的不是写错一次,而是漏掉一个看似无害的 orderByRaw() 字段拼接,或在某个导出接口里用了 DB::select() 却忘了加问号。安全边界不在 ORM 名字上,而在每一次 SQL 构造是否交由数据库引擎做结构/数据分离。只要出现字符串拼接 + 用户输入,不管多短,就得停下来补绑定或白名单。


















