eloquent的selectraw()、whereraw()、orderbyraw()、havingraw()、groupbyraw()必须传第二个参数绑定变量,否则用户输入直入sql引发注入;字段名在orderbyraw()等中不可参数化,须白名单校验。

直接用 Eloquent ORM 的常规方法(如 where()、orderBy()、selectRaw() 等)基本不会触发 SQL 注入——前提是别手动拼接用户输入进 SQL 字符串里。真正危险的,是那些名字带 Raw 的方法,以及绕过 ORM 直接写原生查询的场景。
哪些 Raw 方法必须传第二个参数?
selectRaw()、whereRaw()、orderByRaw()、havingRaw()、groupByRaw() 这些函数都支持第二个参数,用于绑定变量。不传这个数组,就等于把用户输入直接塞进 SQL 里执行。
- 错误写法:
$query->whereRaw("name like '%$keyword%'")——$keyword是用户可控的,单引号、括号、UNION SELECT全都能跑起来 - 正确写法:
$query->whereRaw("name like ?", ["%{$keyword}%"])或$query->whereRaw("name like concat('%', ?, '%')", [$keyword]) - 注意:MySQL 的
concat()不影响参数化安全,但不能写成concat('%', '$keyword', '%')—— 只要字符串里出现$变量插值,就已脱离参数化保护范围
为什么不能用 PHP 字符串拼接代替参数绑定?
因为拼接发生在 PHP 层,而数据库预编译是在 SQL 解析阶段完成的。拼进去的字符串会被当作文本的一部分解析,不是“值”。哪怕你加了 addslashes() 或 str_replace("'", "''"),也挡不住多字节编码绕过、宽字节注入、或像 1' AND SLEEP(3) OR '1'='1 这类非引号闭合型攻击。
- ORM 的参数化本质是交给 PDO/MySQLi 做 prepare + execute 分离,数据库引擎明确知道哪部分是结构、哪部分是数据
- 任何手动转义都属于“尽力而为”,而参数化是“机制保障”
- 尤其在
orderByRaw()场景下,字段名不能参数化(SQL 标准不允许),所以必须白名单校验排序字段,比如:in_array($sortField, ['id', 'created_at', 'name'], true)
哪些地方看似安全,实则埋雷?
开发者容易误以为用了 Eloquent 就万事大吉,但以下几处仍高频出问题:
-
DB::select("SELECT * FROM users WHERE name = '$name'")—— 完全绕过 ORM,没走任何绑定逻辑 -
Model::query()->where('status', request('status'))->get()看似安全,但如果status是枚举字段,攻击者传' OR 1=1 --会变成WHERE status = '' OR 1=1 --',而 Laravel 默认对字符串值加单引号,导致语法有效 - 使用
when()链式调用时嵌套whereRaw()却忘了传参,例如:->when($keyword, fn($q) => $q->whereRaw("content like '%$keyword%'")) - 第三方包或自定义 scope 中硬编码了 raw 查询,且未审查参数来源
最常被忽略的一点:orderByRaw() 和 groupByRaw() 的字段名无法参数化,必须做白名单控制;而所有带用户输入的 Raw 调用,第二个参数数组不能为空、不能漏掉对应占位符数量——少一个,就可能让后续参数错位,导致意外拼接。











