Chr*_*ers 18 postgresql security database-design
首先介绍一下背景。
LedgerSMB 项目是一个在 PostgreSQL 上运行的开源财务会计软件项目。我们在用户定义的函数中实现了大量的业务逻辑,它们充当程序对象方法和数据库行为之间的主要映射工具。目前我们使用数据库用户作为身份验证用户,部分是出于选择(这允许集中的安全逻辑,以便可以编写其他工具并重用授予用户的权限),部分是必要的(在我们从 SQL-Ledger 分叉之后,有用于在该代码库上改进安全性的选项并不多)。
这让我们可以访问 PostgreSQL 可以访问的合理数量的单点登录选项,从 LDAP 到 Kerberos 5。我们甚至可以在涉及密码的地方使用 PAM。它还允许我们在与其他应用程序集成或允许其他客户端界面时重用权限。对于财务会计应用程序来说,这似乎是一场胜利。
有明显的成本。对于 Web 应用程序,我们可以支持的 http 身份验证类型非常有限。例如,DIGEST 完全出局了。BASIC 可以工作,我们可以很容易地实现 KRB5(我计划支持它并在 1.4 中开箱即用)。非常强大的身份验证措施无法直接对此进行适当管理,尽管我们可能会在必要时将它们填充(例如 BASIC + 客户端 SSL 证书,其 cn 与用户名和特定的根 ca 匹配)。
与此同时,我们遇到了相当多的批评,主要来自开发人员,偶尔来自 dba 的批评,他们告诉我应用程序应该是安全屏障,而不是数据库。我的观点仍然是,较小的安全边界通常更好,业务逻辑和安全逻辑的重用是相辅相成的,我觉得重用业务逻辑而不在同一级别重用安全逻辑是危险的的程序。
我在这里错过了任何主要的权衡吗?有没有我没有考虑的问题?
Cra*_*ger 18
我认为您将身份验证和授权混为一谈。
我完全同意将安全模型保留在数据库中是明智的,尤其是因为 LedgerSMB 的设计考虑了来自多个客户端的访问。除非您计划使用中间件层实现 3 层,否则将用户作为数据库角色是非常有意义的,尤其是对于会计应用程序之类的东西。
但这并不意味着你必须进行身份验证使用PostgreSQL的支持的验证方法对数据库用户。只有您愿意,您的数据库用户、角色和授权才能用于授权。
例如,以下是它对 Web ui 的工作方式:
jane连接到 web ui 服务器并使用任何所需的方法进行身份验证,例如 HTTPS X.509 客户端证书握手和 DIGEST 身份验证。服务器现在有一个来自它接受的用户的连接jane。
服务器使用固定的用户名/密码(或 Kerberos 或任何您喜欢的)连接到 PostgreSQL,以用户身份向数据库服务器验证其自身webui。数据库服务器信任webui对其用户进行身份验证,因此webui已给予适当的GRANTs(见下文)。
在该连接上,服务器用于SET ROLE jane;承担用户的授权级别jane。直到RESET ROLE;或其他SET ROLE运行时,连接与相同的访问权限运行jane和SELECT current_user()等将报告jane。
服务器维护其所拥有的数据库连接之间的关联SET ROLE,以jane和为用户的web会话jane,不容许该通过与其他用户的其他连接,无需新的使用PostgreSQL的连接SET ROLE其间。
您现在正在服务器外部进行身份验证,但在服务器中保持授权。Pg 需要知道存在哪些用户,但不需要他们的密码或身份验证方法。
看:
webui 服务器控制查询的运行,它不会让jane运行原始 SQL(我希望!)所以jane不能通过RESET ROLE; SET ROLE special_admin_user;web ui。为了增加安全性,我会向拒绝的服务器添加一个语句过滤器SET ROLE,RESET ROLE除非连接位于或进入未分配的连接池。
您仍然可以自由地在其他客户端中对 Pg 使用直接身份验证;你可以自由混合搭配。您只需要为用户授予可以通过 Web 登录GRANT的webui用户的权限SET ROLE,然后为这些用户提供CONNECT您想要的任何正常权限、密码等。如果您想让它们仅用于网络,则REVOKE它们CONNECT对数据库(和来自public)的权利。
为了使这种身份验证/授权分离变得容易,我有一个特殊的角色assume_any_user,我GRANT每个新创建的用户都扮演这个角色。然后GRANT assume_any_user,我会使用受信任的 Web 前端之类的东西使用的真实用户名,赋予他们成为他们喜欢的任何用户的权利。
assume_any_user建立NOINHERIT角色很重要,因此webui用户或其他任何东西都没有自己的特权,只能在SET ROLE真正的用户面前对数据库进行操作。在任何情况下都不应webui是超级用户或数据库所有者。
如果您是连接池,则可以使用SET LOCAL ROLE仅在事务内设置角色,因此您可以在COMMIT或之后将连接返回到池ROLLBACK。请注意,这RESET ROLE仍然有效,因此让客户端运行他们想要的任何 SQL 仍然不安全。
SET SESSION AUTHORIZATION是此命令的相关但更强大的版本。它不需要角色成员资格,但它只是一个超级用户命令。您不希望您的 web ui 以超级用户身份连接。它可以用 反转RESET SESSION AUTHORIZATION,SET SESSION AUTHORIZATION DEFAULT或者SET SESSION AUTHORIZATION theusername重新获得超级用户权限,因此它也不是降低特权的安全障碍。
SET SESSION AUTHORIZATION如果您是角色成员但不是超级用户,则可以使用类似但不可逆的命令会很棒。目前还没有,但如果你小心的话,你仍然可以很好地分离身份验证和授权。
CREATE ROLE dbowner NOLOGIN;
CREATE TABLE test_table(x text);
INSERT INTO test_table(x) VALUES ('bork');
ALTER TABLE test_table OWNER TO dbowner;
CREATE ROLE assume_any_user NOINHERIT NOLOGIN;
CREATE ROLE webui LOGIN PASSWORD 'somepw' IN ROLE assume_any_user;
CREATE ROLE jane LOGIN PASSWORD 'somepw';
GRANT jane TO assume_any_user;
GRANT ALL ON TABLE test_table TO jane;
CREATE ROLE jim LOGIN PASSWORD 'somepw';
GRANT jim TO assume_any_user;
Run Code Online (Sandbox Code Playgroud)
现在连接为webui. 请注意,你不能做任何事情test_table,但你可以 SET ROLE到jane和然后你可以访问test_table:
$ psql -h 127.0.0.1 -U webui regress
Password for user webui:
regress=> SELECT session_user, current_user;
session_user | current_user
--------------+--------------
webui | webui
(1 row)
regress=> SELECT * FROM test_table;
ERROR: permission denied for relation test_table
regress=> SET ROLE jane;
SET
regress=> SELECT session_user, current_user;
session_user | current_user
--------------+--------------
webui | jane
(1 row)
regress=> SELECT * FROM test_table;
x
------
bork
(1 row)
Run Code Online (Sandbox Code Playgroud)
请注意,webui 可以 SET ROLEto jim,即使已经SET ROLEd tojane并且即使jane还没有被GRANT授予担任角色的权利jim。SET ROLE设置您的有效用户 ID,但它不会取消您SET ROLE对其他角色的能力,这是您连接的角色的属性,而不是您当前的有效角色。因此,您必须仔细控制对SET ROLE和RESET ROLE命令的访问。AFAIK,没有办法永久SET ROLE建立连接,真正成为目标用户,尽管拥有它当然很好。
相比:
$ psql -h 127.0.0.1 -U webui regress
Password for user webui:
regress=> SET ROLE jane;
SET
regress=> SET ROLE jim;
SET
regress=> SELECT session_user, current_user;
session_user | current_user
--------------+--------------
webui | jim
(1 row)
Run Code Online (Sandbox Code Playgroud)
到:
$ psql -h 127.0.0.1 -U jane regress
Password for user jane:
regress=> SET ROLE webui;
ERROR: permission denied to set role "webui"
regress=> SET ROLE jim;
ERROR: permission denied to set role "jim"
Run Code Online (Sandbox Code Playgroud)
这意味着这SET ROLE与以给定角色登录并不完全相同,您必须牢记这一点。
webui不能SET ROLE,dbowner因为它没有被GRANT正确编辑:
regress=> SET ROLE dbowner;
ERROR: permission denied to set role "dbowner"
Run Code Online (Sandbox Code Playgroud)
所以就其本身而言,它是非常无能为力的,它只能承担其他用户的权限,并且只有当这些用户启用了 Web 访问权限时。