限制Web应用程序的PostgreSQL权限

18

目标

创建一个数据库,其中包括三个用户,并限制他们的权限(我只是在胡思乱想,所以我的用户分离也可以更正):

  1. 超级用户 - 这个用户允许对数据库进行最初的配置。创建应用程序数据库,创建其他用户,设置他们的权限。默认的 postgres 超级用户适用于我,所以这一部分已经完成。
  2. 管理员 - 这个用户仅有权限访问在配置期间创建的数据库。管理员可以 CRUD 所有表中的所有数据,并且还可以 CRUD 表等等。这是一种“仅针对此数据库的超级用户”的情况。当应用程序正在更新时,管理员是自动化工具使用的用户,用于处理数据库迁移。
  3. 应用程序用户 - 这个用户最终支持 Web 应用程序的功能。请注意,这与网页上的用户等无关 - 这是服务器利用来运行查询、插入和删除数据的用户。我明确地希望这个用户不能修改任何权限,也不能创建/销毁表或索引或任何结构性内容。

我尝试过的

首先,查看(通常优秀的)PostgreSQL文档,授权页面几乎让我眼花缭乱。在阅读了几个小时有关PostgreSQL角色和权限的内容后,我感到晕头转向。我认为再多做一点工作,我就能确定管理员用户所需的内容,但是我对“应用用户”感到非常困惑。我已经完成了这一步(名称和密码都只是占位符):

$ psql -U postgres
postgres=# CREATE USER "app-admin" WITH PASSWORD 'password';
CREATE ROLE
postgres=# CREATE USER "app-user" WITH PASSWORD 'password';
CREATE ROLE
postgres=# CREATE DATABASE "test-database" WITH OWNER "app-admin";
CREATE DATABASE
postgres=# \c "test-database"
You are now connected to database "test-database" as user "postgres".
test-database=# DROP SCHEMA "public";
DROP SCHEMA
test-database=# CREATE SCHEMA "app" AUTHORIZATION "app-admin";
CREATE SCHEMA

这里是我不确定的地方。我觉得我想要避免的答案是“默认撤销所有权限,然后枚举你在所有不同对象的所有不同级别上所需要的所有特权”。我想避免这个答案,因为我根本不知道我需要什么。如果那最终成为了答案,那我就只能花更多时间去阅读,但通常当我开始走这样的路时,我会错过一些东西。
问题
如何限制app-user的权限,使其无法修改任何结构数据(例如不能添加或销毁表),但仍能连接并对行执行任何操作(行级安全性甚至不在我的考虑范围内)。这种权限的一般模型是否真的与PostgreSQL的期望不太一致?如果我必须遍历“授予”页面上的每个选项来完成这样的事情 - 无论是我做这件事的动机还是我采取的方式 - 我感觉自己可能已经错过了什么。
背景

我正在尝试构建我的第一个端到端网络应用程序。我已经完成了足够的通用软件开发和网络应用程序开发,现在我正在尝试理解我平常认为理所当然的部分。在考虑最小权限原则的情况下,我正在尝试设置PostgreSQL服务器。

支线任务

我还没有在我曾加入开发团队的网络应用程序中看到过这样的做法,尽管它们通常很小且使用不多。这么做实际上会有什么效果吗?是否有令人信服的理由来解释为什么要这样做或者这是一个糟糕或无效的想法?我的假设是,如果最终出现了SQL注入漏洞,这将减轻损害,因为数据库用户的访问权限受到限制。这个想法是错误的吗?

我发现的有关主题的好文章:


注:这些链接提供了与PostgreSQL相关的安全性和访问控制的信息。
2个回答

18

首先,我将回答您的“支线任务”问题:

您对自己的担忧和关注是完全正确的,每个设计应用程序的人都应该考虑同样的事情。其他任何不注意细节的做法都是粗心大意的。

为了减轻成功的SQL注入攻击可能造成的损害,您应该绝对采用最小特权原则。

设置一个符合您要求的系统应该非常简单。

我将使用您示例中的对象名称,但我将使用下划线代替破折号。在对象名称中仅使用小写字母、下划线和数字是良好的实践,因为这会让您的生活更加轻松。

/* create the database */
\c postgres postgres
CREATE DATABASE test_database WITH OWNER app_admin;
\c test_database postgres

/* drop public schema; other, less invasive option is to
   REVOKE ALL ON SCHEMA public FROM PUBLIC */
DROP SCHEMA public;
/* create an application schema */
CREATE SCHEMA app AUTHORIZATION app_admin;
/* further operations won't need superuser access */
\c test_database app_admin
/* allow app_user to access, but not create objects in the schema */
GRANT USAGE ON SCHEMA app TO app_user;

/* PUBLIC should not be allowed to execute functions created by app_admin */
ALTER DEFAULT PRIVILEGES FOR ROLE app_admin
   REVOKE EXECUTE ON FUNCTIONS FROM PUBLIC;

/* assuming that app_user should be allowed to do anything
   with data in all tables in that schema, allow access for all
   objects that app_admin will create there */
ALTER DEFAULT PRIVILEGES FOR ROLE app_admin IN SCHEMA app
   GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_user;
ALTER DEFAULT PRIVILEGES FOR ROLE app_admin IN SCHEMA app
   GRANT SELECT, USAGE ON SEQUENCES TO app_user;
ALTER DEFAULT PRIVILEGES FOR ROLE app_admin IN SCHEMA app
   GRANT EXECUTE ON FUNCTIONS TO app_user;

但如果你认真地遵循最小权限原则,你应该单独授予表格权限,例如不允许app_user在不需要的表格中删除和更新数据。


1
感谢您提供如此详尽的解释!与一般工业软件开发一致,似乎现状更接近于光谱的“粗心大意”端而非另一端。我想继续沿用这个思路,将“管理”和“非特权”连接池分开,以区分应用程序的常规用户和需要添加/编辑/删除用户及其权限等管理员之间的差异。我之前没有真正考虑到通过“最小特权原则”映射到该级别,但这是有道理的。 - user2152081
这听起来像是你混淆了“应用程序用户”(登录应用程序的用户)和数据库用户(应用程序用于连接数据库的用户)。或者你每个应用程序用户都有一个数据库用户吗?如果仅仅因为在应用程序中选中了“管理员用户”复选框就要切换到不同的数据库用户,那可能会很困难。如果你过度追求这一点,最终可能会得到非常复杂的东西,这对安全性也不利。 - Laurenz Albe
我正在尝试将你关于“单独授予权限”的评论与应用程序层面的实际情况协调一致。如果我有两个不同类别的特权用户,其中一个类别具有另一个类别没有的权限超集(例如管理员可以修改所有常规数据,但他们也可以与普通用户无法接触的表交互),将其拆分为两个连接池,一个比另一个具有更多的权限,这样做是否有意义?我知道你可以一直说“这是有意义的”,直到你有1对1的数据库用户对应应用程序用户,所以我正在寻求平衡。 - user2152081
我明白了。在“个人权限”中,我并不是指“为不同的应用程序用户提供不同的权限”,而是“与其允许app_user使用默认特权执行所有操作,不如在每个CREATE TABLE之后添加一个GRANT语句,以授予app_user所需的权限”。我认为,使用单个数据库用户登录应用程序是一种好的实践。 - Laurenz Albe
啊,我之前误解了。我很快会尝试一下 - 非常感谢你的帮助! - user2152081
2
我在尝试过这个之后从未更新过,对我来说它完全按照预期工作。每次为Web应用程序设置新数据库时,我都会回到这个答案。如果可以的话,我会重新点赞! - user2152081

4
对于Web应用程序,我将权限分为三个角色,每个角色都继承自前一个角色。
  1. 只读 - 用于SELECT查询和函数调用
  2. 插入 - 用于INSERT语句
  3. 更新和删除 - 这些主要用于管理,因为公共前端应用程序通常不会修改或删除数据
这样,即使某些黑客成功进行SQL注入,他也仅限于使用的角色权限,通常只有SELECT或INSERT。
我的Web应用程序通常不需要更具侵入性的权限,如CREATE、DROP、TRUNCATE等,因此我不向Web应用程序授予这些权限。
在极少数情况下,第二个角色需要更新或删除某些内容时,我要么给它特定表的权限,要么将代码放入使用SECURITY DEFINER创建的函数中。
/** role_read is read-only with SELECT and EXECUTE */
CREATE ROLE role_read;
/** role_read_add adds INSERT */
CREATE ROLE role_read_add;
/** role_read_add_modify adds UPDATE and DELETE */
CREATE ROLE role_read_add_modify;


GRANT USAGE ON SCHEMA <schema> TO role_read;

/** for existing objects */
GRANT SELECT  ON ALL TABLES    IN SCHEMA <schema> TO role_read;
GRANT SELECT  ON ALL SEQUENCES IN SCHEMA <schema> TO role_read;
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA <schema> TO role_read;

/** for future objects */
ALTER DEFAULT PRIVILEGES IN SCHEMA <schema>
    GRANT SELECT ON TABLES TO role_read;

ALTER DEFAULT PRIVILEGES IN SCHEMA <schema>
    GRANT SELECT ON SEQUENCES TO role_read;

/** role_read_add inherits from role_read */
GRANT role_read TO role_read_add;

/** for existing objects */
GRANT INSERT ON ALL TABLES IN SCHEMA <schema> TO role_read_add;
GRANT ALL ON ALL SEQUENCES IN SCHEMA <schema> TO role_read;

/** for future objects */
ALTER DEFAULT PRIVILEGES IN SCHEMA <schema>
    GRANT INSERT ON TABLES TO role_read_add;

ALTER DEFAULT PRIVILEGES IN SCHEMA <schema>
    GRANT ALL ON SEQUENCES TO role_read_add;

/** role_read_add_modify inherits from role_read_add */
GRANT role_read_add TO role_read_add_modify;

/** for existing objects */
GRANT UPDATE, DELETE ON ALL TABLES IN SCHEMA <schema> 
    TO role_read_add_modify;

/** for future objects */
ALTER DEFAULT PRIVILEGES IN SCHEMA <schema>
    GRANT UPDATE, DELETE ON TABLES TO role_read_add_modify;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接