db::raw()仅标记sql片段绕过转义,不执行语句;必须嵌入select()/whereraw()等方法才生效,且只封装结构(如函数、运算符),绝不拼接用户输入,动态值须通过参数绑定传入。

DB::raw() 本身不执行 SQL,只负责绕过转义
DB::raw() 是个标记器,不是执行器。它告诉查询构建器“这段字符串我信,别动它”,但不会触发任何数据库操作。常见误用是把它当 DB::select() 用,比如 DB::raw("SELECT * FROM users") —— 这只会生成一个 RawExpression 对象,毫无效果。
真正起作用的是它嵌入的位置:在 select()、whereRaw()、orderByRaw() 等方法里,它才被拼进最终 SQL。所以安全的前提是:它只包裹结构(函数、运算符、字段名),绝不包裹用户输入的值。
- ✅ 安全:
DB::raw("DATE_FORMAT(created_at, '%Y-%m')")—— 无变量,纯结构 - ❌ 危险:
DB::raw("name = '{$request->name}'")—— 直接拼接,等同裸奔 - ⚠️ 模糊地带:
DB::raw("CONCAT(first_name, ' ', {$middle})")——$middle应走绑定,不能塞进 raw 字符串
whereRaw() 和 selectRaw() 是唯一需手动防注入的入口
标准 where()、whereIn()、update() 全部自动绑定,天然免疫注入。whereRaw() 和 selectRaw() 则把 SQL 构造权交给你,稍有不慎就中招。
正确写法必须拆开:SQL 结构写在 raw 字符串里,动态值一律通过第二个参数数组传入,由 PDO 预处理绑定:
- ✅ 正确:
->whereRaw('created_at > DATE_SUB(NOW(), INTERVAL ? DAY)', [7]) - ✅ 正确:
->whereRaw('JSON_CONTAINS(meta, ?)', [json_encode(['tag' => 'hot'])]) - ❌ 错误:
->whereRaw("email LIKE '%{$q}%'")—— 单引号和 % 全被当 SQL 解析 - ❌ 错误:
->orderByRaw("{$sortField} {$sortDir}")—— 字段名无法绑定,必须白名单校验
动态列名/表名必须白名单校验,不能靠 (int) 强转
占位符 ? 只能绑值(values),不能绑标识符(identifiers)——列名、表名、排序字段、GROUP BY 字段都不支持参数化。试图用 (int)$column 或 filter_var($table, FILTER_SANITIZE_STRING) 是无效防护。
攻击者传 "1; DROP TABLE users--",强转后还是 1,但如果这个值后续用于日志拼接、缓存 key 或其他上下文,隐患仍在。真正有效的做法是运行时白名单校验:
- ✅ 推荐:
in_array($sortField, ['name', 'email', 'created_at'], true) - ✅ 推荐:
$validColumns = [2022 => 'ElrA2022', 2023 => 'ElrA2023']; $column = $validColumns[$year] ?? throw new InvalidArgumentException(); - ✅ 请求级验证:
'variant_id' => 'required|integer|in:1,2,3'—— Laravel 自动拒绝非法值并返回 422 - ❌ 不可靠:
"table3.ElrA{$effectiveYear}"直接拼接,哪怕$effectiveYear是 int
命名绑定在 DB::select() 中需显式传 PDO 选项
DB::select() 支持命名绑定(如 :name),但默认不生效,必须手动传入 PDO 选项:[PDO::ATTR_EMULATE_PREPARES => true]。否则命名绑定会被忽略,导致查不到数据且无报错,尤其在 MySQL 非严格模式下极难发现。
更稳妥的做法是统一用问号占位符,避免额外配置:
- ✅ 简洁安全:
DB::select('SELECT * FROM users WHERE status = ? AND role IN (?, ?)', [1, 'admin', 'editor']) - ⚠️ 易踩坑:
DB::select('SELECT * FROM users WHERE name = :name', ['name' => 'Alice'])—— 不加第三个参数,绑定失效 - ⚠️ IN 子句陷阱:
WHERE id IN (?)会报错 —— PDO 不支持数组绑定,得手动拼占位符:"WHERE id IN (" . implode(',', array_fill(0, count($ids), '?')) . ")"
复杂点在于:白名单校验和参数绑定是两套机制,不能互相替代;列名校验必须在 PHP 层完成,而值绑定交给 PDO。漏掉任一环,都可能让注入在看似“已防护”的地方悄然发生。











