Laravel防SQL注入的核心是参数化查询:DB::table()和Eloquent的where系列方法默认安全;raw()和DB::select()若拼接用户输入则危险,必须用?占位符或命名参数绑定。

直接用 DB::table() 或 Eloquent 的查询构造器,只要不拼接用户输入进 SQL 字符串,Laravel 就默认帮你做了参数化 —— 这是防 SQL 注入最核心的保障。
为什么 raw() 和 DB::select() 容易中招
这两个方法会绕过 Laravel 的参数绑定机制,把字符串原样送进 PDO。一旦你把 $_GET['id'] 或 request('name') 直接拼进 raw() 里,就等于亲手打开注入大门。
-
DB::select("SELECT * FROM users WHERE name = '" . request('name') . "'")→ 危险! -
DB::table('users')->whereRaw("name = '" . request('name') . "'")→ 危险! - 正确写法是用占位符 + 参数数组:
DB::select("SELECT * FROM users WHERE name = ?", [request('name')]) - 或改用查询构造器:
DB::table('users')->where('name', request('name'))->get()
Eloquent 中 where() 系列方法天然安全
所有 where()、whereIn()、whereNotNull() 等方法,底层都走 PDO 预处理绑定,用户输入自动转为参数,不会被解释为 SQL 语法。
-
User::where('email', request('email'))->first()→ 安全 -
User::whereIn('id', request('ids', []))->get()→ 安全(即使ids是数组) - 但
User::whereRaw("email = '" . request('email') . "'")->first()→ 不安全,除非你手动绑定:whereRaw("email = ?", [request('email')])
自定义 SQL 必须用 ? 占位符或命名参数
哪怕你非得写原生 SQL,也必须让参数和语句分离。PDO 支持两种方式,Laravel 全兼容:
- 问号占位:
DB::select("SELECT * FROM posts WHERE status = ? AND created_at > ?", ['published', $date]) - 命名参数:
DB::select("SELECT * FROM posts WHERE status = :status AND created_at > :date", ['status' => 'published', 'date' => $date]) - 别用字符串拼接,哪怕只拼一个 ID:
"id = " . $id在数字上下文里看似无害,但一旦字段类型变化或数据库配置宽松(如 MySQL 的宽松模式),就可能被绕过
DB::select("SELECT * FROM users WHERE id = ? AND role = ?", [$id, $role]);真正容易被忽略的点:批量操作(如 upsert()、insert())虽然接受数组,但只要数据来自用户输入,就得先过滤或验证类型;而 DB::unprepared() 这种执行任意 SQL 的函数,完全不绑定参数,绝对不要传任何动态内容进去。










