在共享 postgres 池中实施行级安全性

问题描述 投票:0回答:1

我正在开发一个典型的客户端-服务器网络应用程序。它使用类似于 GraphQL 的系统,客户端可以灵活地指定所需的数据,而无需为每种类型的数据提供自定义 API 端点。服务器正在运行节点,并使用具有典型

pg.Pool
的node-postgres。客户可以发送如下内容:

{select: '*', from: 'expenses', where: {'op': 'gt', 'lhs': 'expenses.amount', 'rhs': 20}}

这将被翻译为

SELECT * FROM expenses WHERE expenses.amount > $1
(给定
$1
= 20)。只要足够小心,该系统就可以免受注入攻击。

我还想合并行级安全策略。例如:

create policy only_see_own_expenses on expenses using (expenses.user_id = <USER ID>);

作为额外的安全屏障,我想确保即使注入攻击成功,客户端也无法“取消设置”其用户 ID。

我已经看到

<USER ID>
有几种定义方式:

  1. current_user
    ,在这种情况下,应用程序的每个用户还需要一个 postgres 用户/角色
  2. 任意设置,如
    current_setting('myapp.user_id')
    与交易开始时的
    SET LOCAL myapp.user_id = ...
    组合

方法(2)对我来说似乎最灵活。我只需将每个生成的 SQL 查询包装在

BEGIN; SET LOCAL myapp.user_id = 123; {generated query}; END;
中。问题是攻击者可以注入另一个
SET LOCAL
语句,并冒充另一个用户。

在方法(1)中,您可以类似地在开始时使用

SET ROLE ...
语句包装每个生成的查询,从而产生相同的问题。另一种方法是为具有该特定角色的每个查询创建一个新连接。我相信 postgres 永远不会允许该连接切换到另一个角色。但是为每个查询设置一个新连接会导致大量开销。

如何强制执行行级安全性而不影响每个查询的新连接的性能?

postgresql sql-injection node-postgres row-level-security
1个回答
0
投票

正如您所观察到的,但是设置占位符参数并使用

SET LOCAL ROLE
临时承担不同的角色可能会被能够执行任意 SQL 的攻击者破坏,就像在 SQL 注入攻击中一样。

我认为没有一种方法可以安全地避免 SQL 注入来完成您想要的任务。问题是一个基本问题:您在应用程序中处理身份验证,而不是在数据库中(您使用单个应用程序用户从连接池中受益),但您希望数据库通过行级安全性处理授权。这需要应用程序通过某种方式告诉数据库应用程序用户是什么。现在,应用程序可以告诉数据库任何信息的唯一方法是使用 SQL,而可以运行任意 SQL 语句的攻击者总是可以破坏这一点。

我认为您唯一的选择是强化您的应用程序以抵御 SQL 注入攻击。

© www.soinside.com 2019 - 2024. All rights reserved.