Web 应用程序的受限 PostgreSQL 权限

Restricted PostgreSQL permissions for web app

目标

创建三个用户的数据库并限制他们的权限(我只是想出声,所以我的用户分离也有待更正):

  1. 超级用户 - 此用户允许对数据库进行最初始的配置。创建应用程序数据库,创建其他用户,设置他们的权限。默认 postgres 超级用户为我工作,所以这个就完成了。
  2. 管理员 - 此用户只能访问在配置期间创建的数据库。管理员可以CRUD所有表中的所有数据,也可以CRUD表等"Superuser for only this database"类型的情况。更新应用程序时,管理员是自动化工具用来处理数据库迁移的用户。
  3. 应用程序用户 - 此用户最终是支持网络应用程序功能的用户。请注意,这与网页上的用户等无关 - 这是服务器用来 运行 查询、插入和删除数据的用户。我明确地希望此用户能够修改任何内容的权限,也不希望create/destroy表或索引或任何结构。

我试过的

首先,查看(总体来说非常出色的)PostgreSQL 文档,page on Grant 几乎让我感到不解。在花了几个小时阅读有关 PostgreSQL 角色和特权的内容后,我通常感到困惑。我认为通过更多的工作,我将能够为管理员用户确定我想要的内容,但我仍然停留在 "app user" 上。我已经了解了这么多(命名和密码都只是占位符):

$ 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

这就是我不确定的地方。我觉得我想避免的答案是 "revoke everything by default then enumerate all the privileges you'll need at all the different levels on all the different objects"。我试图避免这种情况,因为我直接不知道我在那里需要什么。如果这最终成为答案,那么我只需要蹲下并阅读更多内容,但通常当我开始沿着这样的道路走下去时,我会错过一些东西。

问题

如何限制 app-user 的权限,使他们无法修改任何结构数据(例如,无法添加或销毁表)但能够连接行并对行执行任何操作(行级安全性甚至没有在我的雷达上)。这种一般的特权模型是否与 PostgreSQL 所期望的并不真正同步?如果我必须遍历那个 "grant" 页面上的每个选项来完成这样的事情,我觉得我错过了一些东西——无论是我最初做这件事的动机还是我做这件事的方式去吧。

上下文

我正在尝试构建我的第一个端到端网络应用程序。我已经完成了足够多的通用软件开发和 Web 应用程序开发,现在我正在尝试理解我通常每天认为理所当然的部分。我正在尝试设置 PostgreSQL 服务器,同时牢记 principle of least privilege

支线任务

我还没有在我刚刚加入开发团队的 Web 应用程序上看到这样做,尽管它们通常很小而且使用不多。这样做真的有什么用吗?有没有人有令人信服的理由来说明为什么要做这样的事情,或者为什么这是一个坏主意或无效的主意?我的假设是,如果我最终以 SQL 注入漏洞告终,这将减轻损害,因为数据库用户的访问权限将受到限制。这是误导了吗?

我发现的有关该主题的精彩文章:

我先回答你的“支线任务”问题:

您的担心和担忧是完全正确的,每个设计应用程序的人都应该考虑同样的事情。其他的都是草率和粗心。

为了减轻成功的 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;

但是,如果您采取最不认真的原则,您应该单独授予 table 权限,例如不允许 app_userDELETEUPDATE 中的数据 table,因为用户不需要这样做。

对于 Web 应用程序,我将权限分为三个角色,每个角色都继承其前身。

  1. 只读 - 用于 SELECT 查询和函数调用
  2. 插入 - 用于 INSERT 语句
  3. 更新和删除 - 这些主要用于管理,因为 public 面向前端的应用程序通常不会修改或删除数据

这样,即使某些黑客设法进行 SQL 注入,他也仅限于所使用角色的权限,通常只有 SELECT 或 INSERT。

我的 Web 应用程序通常不需要更具侵入性的权限,例如 CREATE、DROP、TRUNCATE 等,因此我不会向 Web 应用程序授予这些权限。

在极少数情况下,第二个角色需要更新或删除某些内容,我要么授予它对特定 table 的权限,要么将代码放入使用 [=11= 创建的函数中].

/** 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;