PostgreSQL.这样的功能容易受到 SQL 注入的攻击还是安全的?

PostgreSQL. Is such function vulnerable to SQL injection or is it safe?

看起来有问题的函数

我正在探索 postgresql 数据库,我看到了一个重复出现的模式:

CREATE OR REPLACE FUNCTION paginated_class(_orderby text DEFAULT NULL, _limit int DEFAULT 10, _offset int DEFAULT 0)
RETURNS SETOF pg_class
LANGUAGE PLPGSQL
AS $$
BEGIN
        return  query  execute'
            select * from pg_class
        order by '|| coalesce (_orderby, 'relname ASC') ||'
        limit  offset 
        '
        USING _limit, _offset;
END;
$$;

示例用法:

SELECT * FROM paginated_class(_orderby:='reltype DESC, relowner ASC ')

重复是:

DB Fiddle 为:https://www.db-fiddle.com/f/vF6bCN37yDrjBiTEsdEwX6/1

问题:这样的函数是否易受SQL注入攻击?

根据外部迹象,可以怀疑此类功能容易受到 sql 注入攻击。

但是我所有寻找参数组合的尝试都失败了。

例如

CREATE TABLE T(id int);
SELECT * FROM paginated_class(_orderby:='reltype; DROP TABLE T; SELECT * FROM pg_class');

will return “查询错误:错误:无法打开多查询计划作为游标”.

我没有找到利用 UPDATE/INSERT/DELETE.

漏洞的方法

那么我们可以断定这样的功能实际上是安全的吗?

如果是这样:那为什么?

更新。可能的攻击计划

也许我不清楚:我不是在询问一般准则,而是在询问对漏洞的实验性利用或证明这种利用是不可能的。

DB Fiddle 为此:https://www.db-fiddle.com/f/vF6bCN37yDrjBiTEsdEwX6/4(当然你也可以提供其他的)

到目前为止我的结论

一个。如果 _orderby 将具有以下部分,则这种攻击是可能的:

  1. sql 抑制第一个 SELECT
  2. 输出的代码
  3. 做一些有害的事情
  4. select * from pg_class 这样就满足 RETURNS SETOF pg_class

例如

SELECT * FROM paginated_class(_orderby:='relname; DELETE FROM my_table; SELECT * FROM pg_class')

2 和 3 很容易。我不知道第 1 部分的方法。

这将生成:“错误:无法作为游标打开多查询计划”

乙。如果不能先压制SELECT

然后:

所以呢?我们可以断定这样的功能实际上是安全的吗?

或者我遗漏了什么?

更新 2. 使用准备好的函数进行攻击

来自回答

一个。有可能实施拒绝服务攻击投入昂贵的计算

乙。副作用:

If you put a function with side effects into the ORDER BY clause, you may also be able to modify data.

让我们试试后者:

CREATE FUNCTION harmful_fn()
RETURNS bool
LANGUAGE SQL
AS '
DELETE FROM my_table;
SELECT true;
';

SELECT * FROM paginated_class(_orderby:='harmful_fn()', _limit:=1);

https://www.db-fiddle.com/f/vF6bCN37yDrjBiTEsdEwX6/8

是的。

因此,如果攻击者有权创建函数:非 DOS 攻击也是可能的。

我接受 Laurenz Albe 的回答但是:是否可以在没有功能的情况下进行非 DOS 攻击?

想法?

不,那不安全。攻击者可以通过 _orderby 参数将任何代码放入您的 ORDER BY 子句中。

例如,您可以传递任意子查询,只要它 returns 只有一行:(SELECT max(i) FROM generate_series(1, 100000000000000) AS i)。如果查询的开销足够大,那么它可以很容易地用于拒绝服务攻击。或者,就像这个例子一样,您可以用临时文件引起(短暂的)超出 space 的情况。

如果将具有副作用的函数放入 ORDER BY 子句中,您也许还可以修改数据。