位置:首页 > SQL > Laravel 项目中如何修复 SQL 注入漏洞

Laravel 项目中如何修复 SQL 注入漏洞

时间:2026-08-27  |  作者:冻月看渠  |  阅读:0

目录

  1. 前言
  2. Laravel 默认安全到什么程度
  3. `whereRaw()` 为什么容易出问题
  4. 动态列名和排序字段为什么不能参数化
  5. `DB::select()` 和原生查询该怎么写
  6. 为什么说 ORM 安全不等于项目安全
  7. 一份可直接落地的排查清单

前言

Laravel 并不是天然不会出 SQL 注入,真正的风险通常出在 `whereRaw()`、`orderByRaw()`、`DB::select()` 这类绕过默认保护的写法。本文从参数绑定、动态字段白名单和原生查询占位符三条主线出发,帮你快速定位并修复项目里的高危查询。

Laravel 项目中如何修复 SQL 注入漏洞 的核心流程信息图
Laravel 项目中如何修复 SQL 注入Laravel 并不是天然不会出 SQL 注入,真正的风险通常出在。

Laravel 项目里,大部分常规查询天生就比较安全,但这并不代表项目不会出现 SQL 注入。本文重点回答三个实际问题:哪些写法最危险、如何把现有代码改成安全版本、动态字段和原生 SQL 该怎么处理。

如果你正在排查接口查询、搜索、排序或后台筛选功能,这篇内容可以直接作为检查清单使用。

Laravel 默认安全到什么程度

只要没有手动把用户输入拼进 SQL 字符串,Laravel 默认提供的查询构造器和 ORM 通常是安全的。真正需要警惕的,是主动绕过这层保护的写法,例如 whereRaw()selectRaw()orderByRaw(),以及直接使用 DB::select() 拼接 SQL。

问题的关键不在于有没有用 Laravel,而在于用户输入是否被当作 SQL 结构的一部分执行。一旦直接拼接进去,单引号、UNION SELECTSLEEP(3) 这类注入载荷就有机会生效。

`whereRaw()` 为什么容易出问题

危险点在哪里

whereRaw() 本身不是不能用,问题在于很多项目只写了 SQL 片段,却没有传第二个参数绑定变量。这样等于把用户输入直接交给数据库执行。

白底信息图,对比 Laravel 中危险 Raw 查询与安全参数绑定写法
参数绑定与拼接风险对比用正反示例说明 `whereRaw()` 中字符串拼接与参数绑定的区别。
$query->whereRaw("name like '%$keyword%'")

上面这类写法里,$keyword 是用户可控值,完全暴露。

安全改法是什么

正确方式是使用参数绑定,把用户输入作为“值”传给占位符,而不是拼到 SQL 文本中。

$query->whereRaw("name like ", ["%{$keyword}%"])
$query->whereRaw("name like concat('%', , '%')", [$keyword])

这两种写法都可以。这里要注意,concat() 本身不会破坏参数化,但只要你又回到 PHP 字符串插值,保护就失效了。

concat('%', '$keyword', '%')

像这样把变量直接插进字符串,看起来只是换了一种写法,实际已经脱离参数绑定。

动态列名和排序字段为什么不能参数化

很多开发者会在搜索、排序、报表导出里动态切换字段,这里是 Laravel 项目里最常见、也最容易被误判的一类风险。

要点很明确:字段名、表名、排序字段不能参数化。原因不是 Laravel 限制,而是 SQL 标准和 PDO 的工作方式决定了占位符只能绑定“值”,不能绑定 SQL 语法结构。

下面这类代码就有风险:

DB::table('products')->select("product_variant_{$request->input('id')}")

如果用户传入 1 FROM users --,就可能触发异常,严重时还可能被进一步利用。

正确处理方式

这类场景不要尝试“参数化字段名”,而是直接做白名单校验或映射。

in_array($field, ['id', 'name', 'created_at'], true)
$columns = [1 => 'product_variant_1', 2 => 'product_variant_2']

如果请求参数本身就应该受限,最好把验证前置到请求层:

$request->validate(['sort' => 'required|in:id,name,created_at'])

这比在查询阶段临时兜底更稳,也更容易维护。

`DB::select()` 和原生查询该怎么写

一旦绕过 ORM 和查询构造器,Laravel 不会自动替你处理所有转义问题。像 DB::select()DB::statement() 这样的原生接口,是否安全基本取决于你有没有老老实实写占位符。

高危写法如下:

DB::select("SELECT * FROM users WHERE name = '" . $request->input('name') . "'")

这相当于直接绕过了框架提供的保护。

合规写法应该是:

DB::select("SELECT * FROM users WHERE name = ?", [$request->input('name')])

同样需要注意的是,DB::raw() 并不会让用户输入自动变安全。下面这种包一层单引号的方式也挡不住注入:

DB::raw("'$request->input('sort')'")

为什么说 ORM 安全不等于项目安全

很多 Laravel 项目的问题,不是出在常规的 where()first()paginate(),而是出在“局部绕过”。例如:

  • whereRaw() 被包在 when() 里时,开发者忘了传绑定参数;
  • 动态排序字段在 orderByRaw() 中直接拼接;
  • 验证规则如 ignore() 一类场景里,字段名或结构值来源不明确;
  • 看似只是做搜索高亮、模糊匹配、统计字段切换,实际把用户输入带进了 SQL 结构。

所以安全边界并不只在框架层,而在每一处“用户输入”和“SQL 结构”相遇的地方。

一份可直接落地的排查清单

  • 检查项目中所有 whereRaw()selectRaw()orderByRaw() 是否都使用了绑定参数。
  • 检查 DB::select()DB::statement() 是否存在字符串拼接。
  • 检查动态列名、表名、排序字段是否使用白名单或映射数组。
  • 检查请求验证层,能否通过 in: 规则提前限制可选字段。
  • 检查 DB::raw() 中是否包裹了用户输入。
  • 检查条件分支、查询封装和辅助函数里,是否存在“局部改回拼接 SQL”的情况。

一句话总结:值用参数绑定,结构用白名单控制。只要用户输入开始影响 SQL 语法本身,Laravel 默认的安全能力就帮不上忙了。

免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。

相关文章

更多

精选合集

更多

大家都在玩

热门话题

大家都在看

更多