Laravel默认通过参数绑定防止SQL注入,但手动拼接SQL、使用whereRaw()未绑定、orderBy()字段未校验、$guarded=[]或PDO模拟预处理开启均会导致风险。

只要用 DB::table() 或 Eloquent 的标准查询方法(比如 where()、first()),Laravel 默认就做了参数绑定,SQL 注入风险极低——但前提是别手动拼接 SQL 字符串。
为什么 DB::select() 用问号占位符才安全
直接写原生 SQL 时,DB::select('SELECT * FROM users WHERE id = ?', [$id]) 是安全的;而 DB::select("SELECT * FROM users WHERE id = $id") 就是高危操作。Laravel 在底层把问号占位符交给 PDO 预处理,变量值不会参与 SQL 解析。
- 字符串插值、
sprintf()、str_replace()拼接都绕过预处理,等同于裸奔 - 如果必须动态字段名或表名(如分表场景),不能用问号绑定,得自己白名单校验:
in_array($table, ['logs_2024', 'logs_2025']) -
DB::raw()本身不触发绑定,只做字符串透传,别把它当“安全包装”
where() 和 whereRaw() 的区别不是语法糖,是安全分水岭
User::where('email', $input)->first() 自动转成带绑定的预处理语句;而 User::whereRaw("email = '$input'")->first() 把变量直接塞进 SQL 字符串里,和手拼没区别。
-
where()系列方法(whereIn、whereBetween)全部走绑定,放心用 -
whereRaw()只在真需要复杂表达式时用,且里面所有外部输入必须手动绑定:whereRaw('age > ? AND status = ?', [$minAge, $status]) -
orderBy()的字段名不支持绑定,所以orderBy($request->input('sort'))必须先校验:in_array($sort, ['name', 'created_at'])
Eloquent 的 fill() 和批量赋值不是后门,但得配 $fillable
$user->fill($request->all()) 本身不引发 SQL 注入,但它可能把攻击者伪造的字段(比如 is_admin=1)写进数据库——这不是 SQL 层问题,而是业务逻辑越权。
- 必须显式定义
$fillable = ['name', 'email'],否则fill()/create()全部失效 -
$guarded = []等价于允许所有字段,等于关掉保护,别这么干 - 如果要用
updateOrCreate(),注意第一个参数是 where 条件(自动绑定),第二个是更新数据(受$fillable约束)
最常被忽略的一点:数据库连接配置里的 'options' => [PDO::ATTR_EMULATE_PREPARES => false] 必须开启。Laravel 默认已设,但如果项目手动改过 PDO 配置,模拟预处理打开会导致部分绑定失效——尤其在 MySQL 5.7 以下或特殊字符场景下,看似安全实则裸奔。









