Restricted PostgreSQL permissions for web app Restricted PostgreSQL permissions for web app database database

Restricted PostgreSQL permissions for web app


I'll answer your “side-quest” question first:

you are completely right with your worries and concerns, and everybody who designs an application should think about the same things. Everything else is sloppy and careless.

To mitigate the damage that can be caused by a successful SQL injection attack, you should definitely employ the principle of least privilege.

It should be quite simple to set up a system that matches your requirements.

I'll use the object names from your exaple, except that I'll use underscores instead of minuses. It is good practive to use only lower case letters, underscores and numbers in object names, since it will make your life easier.

/* create the database */\c postgres postgresCREATE 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;

But if you take the principle of least seriously, you should grant table permissions individually and e.g. not allow app_user to DELETE and UPDATE data in tables where there is no need for the user to do so.


For Web Applications, I split the permissions into three roles, where each role inherits from its predecessor.

  1. Read Only - Used for SELECT queries and function calls
  2. Insert - Used for INSERT statements
  3. Update and Delete - These are used mostly for Administration, as the public facing front-end application does not usually modify or deletes data

That way, even if some hacker manages to do SQL Injection he is limited to the permissions of the role that is used, usually only SELECT or INSERT.

My web applications usually do not need the more intrusive permissions like CREATE, DROP, TRUNCATE, etc., so I don't GRANT those permissions to web apps.

In the rare instances where the the second role needs to update or delete something, I either give it permission for that specific table, or put the code in a function that is created with 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;