编程

使用 sole() 阻止 Laravel SQL 注入

9 2026-01-28 02:35:00

我非常喜欢在 Laravel 中使用 Eloquent 的 sole() 方法。如果结果集大于 1,它会抛出异常。这意味着你只能得到一条记录。这通常正是我想要的。除非我确实需要匹配结果集中的第一条记录,否则我已经不再使用 firstOrFail() 方法了。

但我刚刚发现了另一个喜欢使用 sole 方法的理由——它有助于增加一层防止 SQL 注入的保护。让我们来看看它是如何做到的。

所以,在这个例子中,我将编写一些非常容易受到攻击的代码。千万不要这样做。这样做的目的是为了演示纵深防御策略。如果你使用的是标准的 Eloquent 参数化,就不会遇到这个问题。但是,有时情况会变得复杂一些,你必须直接操作 SQL。即使你使用的是参数化查询,使用 sole() 也没有任何问题。你只是在增加一层防御而已。

现在,让我们来设置一下场景。

条件:用户已存在于数据库中,且使用电子邮件地址登录。电子邮件地址是唯一的。由于某些原因,我们必须在查询中使用 whereRaw() 函数(因为这是一个概念验证)。

$incomingInputForEmail = "' OR 1=1 #";
$user = User::whereRaw("email = '$incomingInputForEmail'")->firstOrFail();

$incomingInputForEmail 是用户输入。他们特意在表单字段中输入(或修改了 HTTP 请求)电子邮件地址,使用了 ' OR 1=1 #

事情是这样的:

执行一个查询,条件是电子邮件地址为空字符串,或者 1=1。获取第一个结果。

所以你可以看到,他们根本不需要知道电子邮件地址——这个 SQL 注入就能获取到第一条记录。非常糟糕。

解决方法:当然,我们不想使用 whereRaw(),但又不得不使用。所以,我们用 sole()

记住,电子邮件地址是唯一的。1=1 会返回“所有记录”,因为这是一个正确的 WHERE 条件。

$incomingInputForEmail = "' OR 1=1 #";
$user = User::whereRaw("email = '$incomingInputForEmail'")->sole();

会发生什么?它会抛出 MultipleRecordsFoundException 异常,导致我们无法获取任何用户记录。

总结:所以,不要这样做。相反,应该使用类似 User::where('email', $incomingInputForEmail) 的方法,并且不要使用 whereRaw()。如果确实需要原始查询,请对输入进行清洁。但是,如果只需要一行数据,并且应该只有一行匹配,则可以添加 sole() 来增加一层保护。