Restricted PostgreSQL permissions for web app - database

Goal
Create a database with three users and restrict their privileges (I'm just thinking out loud, so my user separation is also open to correction):
Superuser - this user allows for the very initial provisioning of the database. Create the application database, create the other users, set their privileges. Default postgres superuser works for me, so this one is done.
Administrator - this user has access only to the database that was created during provisioning. Administrator can CRUD all data in all tables, and can also CRUD tables, etc. "Superuser for only this database" type of situation. When the application is being updated, the administrator is the user used by automated tooling to handle database migrations.
App user - this user is ultimately the one who supports the web app's functionality. Note this has nothing to do with users on web pages etc - this is the user the server leverages to run queries, insert and remove data. I explicitly do not want this user to be able to modify permissions of anything, nor create/destroy tables or indices or anything structural.
What I've tried
First off, looking at the (generally excellent) PostgreSQL documentation, the page on Grant pretty much leaves me cross-eyed. After spending a few hours reading about PostgreSQL roles and privileges I'm generally confused. I think with a bit more work I'll be able to nail down what I want for the admin user, but I'm pretty stuck on the "app user". I've gotten about this far (naming and passwords are all just placeholders):
$ 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
And here's where I get unsure. I feel like the answer I'm trying to avoid is "revoke everything by default then enumerate all the privileges you'll need at all the different levels on all the different objects". I'm trying to avoid that because I straight up don't know what I need there. If that ends up being the answer, then I'll just have to hunker down and read a bunch more, but generally when I start going down paths like that I've missed something.
Issues
How do I restrict privileges for app-user so they are unable to modify any structural data (e.g. cannot add or destroy tables) but are able to connect and do anything with rows (row level security is not even on my radar). Is this general model of privileges not really in sync with what PostgreSQL expects? I feel like I'm missing something if I have to walk through every option on that "grant" page to accomplish something like this - whether it be my motivation for doing it in the first place or the means by which I'm going about it.
Context
I'm trying to build my first end-to-end web application. I've done enough general software development and web app development, now I'm trying to understand the pieces that I generally take for granted day to day. I'm trying to set up a PostgreSQL server while keeping the principle of least privilege in mind.
Side-quest
I haven't seen this done on web apps where I have simply joined the development team, although they're generally small and not heavily used. Does doing this actually accomplish anything? Does anyone have compelling reasons for why to do something like this, or why it's a bad or ineffective idea? My assumption was that if I ultimately ended up with a SQL injection vulnerability, this would mitigate the damage because the database user would have limited access. Is that misguided?
Neat articles I've found on the subject:
http://www.ibm.com/developerworks/opensource/library/os-postgresecurity/index.html
PDF WARNING: https://wiki.postgresql.org/images/d/d1/Managing_rights_in_postgresql.pdf
https://www.digitalocean.com/community/tutorials/how-to-use-roles-and-manage-grant-permissions-in-postgresql-on-a-vps--2
http://blog.2ndquadrant.com/auditing-users-and-roles-in-postgresql/

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 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;
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.
Read Only - Used for SELECT queries and function calls
Insert - Used for INSERT statements
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;

Related

Exclusive User ownership on sql server tables

Requirement: User who created tables in a particular schema, should own the tables, other users who got access to that schema should not able to perform any action on that table(including read).
Example:
Tables created by ‘User1’ in ‘Schema1’ should be exclusive to the User1 only with (SELECT, CREATE, UPDATE and DELETE)
Other Users who got access to ‘Schema1’, should not able to perform any actions on the tables created by ‘User1’
This requirement is expected to be available for users who have access to the 'schema1', so the tables they create is accessible only for them and not for other users.
For the sake of the discussion let's CREATE new LOGIN, new SCHEMA, and new USER.
use master
GO
CREATE LOGIN SO_Login WITH PASSWORD = 'Dont1Use2This3In4Production'
GO
Use AdventureWorks2019
GO
CREATE SCHEMA SO_Schema
GO
CREATE USER SO_User FOR LOGIN SO_Login;
GO
In theory, you could get what you are looking for, by simply have a rule which allows CREATE TABLE on specific schema. Something like: GRANT CREATE TABLE ON SCHEMA::SO_Schema TO public;
In this case we could give everyone the option to CREATE TABLE on the schema and use simple DDL trigger on CREATE TABLE in order to add permissions like SELECT,DELETE,INSERT,UPDATE for the user that created the table.
unfortunately, GRANT CREATE TABLE ON SCHEMA is not supported.
To CREATE TABLE you Required to have CREATE TABLE permission in the database and ALTER permission on the SCHEMA in which the table is being created.
https://learn.microsoft.com/en-us/sql/t-sql/statements/create-table-transact-sql?view=sql-server-ver15#permissions-1
This makes the task more complex and probably not recommended in most cases since you will need to provide more permissions than what you really want the USER to have...
If you still want to get this work (against the recommendation) then you will need to GRANT ALTER ON SCHEMA and GRANT CREATE TABLE on database to all - all means "public"
use AdventureWorks2019
GO
GRANT ALTER ON SCHEMA::SO_Schema TO public;
GO
GRANT CREATE TABLE TO public;
GO
next, you will need to DENY the unwonted permission since the above will give all USERs a lot more power than you want to!
This can be done by CREATE DDL TRIGGER on the DATABASE for any DDL_DATABASE_LEVEL_EVENTS
https://learn.microsoft.com/en-us/sql/relational-databases/triggers/ddl-event-groups?view=sql-server-ver15
inside the TRIGGER you should check what was the event. If it was something else than CREATE_SCHEMA or the USER that executed the event should not CREATE SCHEMA then you ROLLBACK TRANSACTION;.
Note! Since you do not want to change the trigger each time a new USER need to CREATE TABLE and add the USER name to the hard coded list of users which can CREATE TABLE, it is best to CREATE new ROLE and simply add each USER you need to this ROLE
CREATE ROLE ModifyTable;
GO
In this case that you based on a ROLE like above ModifyTable, you can GRANT ALTER ON SCHEMA and GRANT CREATE TABLE only to the ROLE ModifyTable instead of to public
In addition, in the same TRIGGER if the USER is one of these that should be able to CREATE the table then you should GRAND him permission to INSERT, DELETE, UPDATE, SELECT on the table which he just created.
Remember that if you forget to DENY a permission from this USER or all the rest then you might have a security issue - which is why this is not recommended procedure.
Your best option is to re-0design the system so you will not need this exact recruitment. So... you can do it as I explained here, but it is not recommended for most cases.
A much better approach is NOT to permit USERs to CREATE TABLEs except for these you can trust with all tables. You should CREATE THE TABLEs for your users directly or using application which you control, and give them the permission to use the specific table which they need. ALTER SCHEMA is not recommended permission to give to simple users!
A user with ALTER permission on a schema can create procedures, synonyms, and views that are owned by the schema's owner. Those objects will have access (via ownership chaining) to information in other schemas owned by the schema's owner. When possible, you should avoid granting ALTER permission on a schema if the schema's owner also owns other schemas.
https://learn.microsoft.com/en-us/sql/t-sql/statements/grant-schema-permissions-transact-sql?view=sql-server-ver15

Postgresql : noInherit not working?

So, I'm trying to figure out how to properly administer a postgresql database.
I'm new with postgres and DBA in general.
I'm currently trying to have a dedicated role for a specific database.
I also want to have other user with a grant on that role so that I could do SET ROLE my_db_role and be able to manipulate this specific database from there.
However, I've got permission leakage, meaning that I can manipulate my database without having to do this SET ROLE my_db_role command.
Here are the commands I do to get my unssuccessful result :
=# CREATE ROLE test NOINHERIT;
=# CREATE USER myuser;
=# CREATE DATABASE test OWNER test;
=# \c test
=# DROP SCHEMA public;
=# CREATE SCHEMA AUTHORIZATION test;
=# GRANT test TO myuser;
=# \c test myuser
=> CREATE TABLE test.mytable(id integer);
CREATE TABLE
Temps : 46,469 ms
Why did the last commands succeeded ?
In my opinion, myuser should have no right on test database/schema, as test role has the NOINHERIT flag, so this CREATE command should not be possible.
It should need to do SET ROLE test to succeed which is not the case here.
What am I missing ?
On a side note, I have a hard time finding good source of information on how to administer properly postgresql apart from the official doc. If you can share some good material about it, you're more than welcome.
I'm afraid I don't know of any particularly good resource covering role management and administration. The standards here show all the signs of having to please several stake-holders and are flexible but confusing.
As to your immediate question though, the issue is that the "NOINHERIT" is on the wrong role. However, this feature is not really a security constraint.
test=# ALTER USER myuser NOINHERIT;
test=# \c - myuser
You are now connected to database "test" as user "myuser".
test=> CREATE TABLE test.mytable(id int);
ERROR: permission denied for schema test
LINE 1: CREATE TABLE test.mytable(id int);
^
test=> SET ROLE test;
SET
test=> CREATE TABLE test.mytable(id int);
CREATE TABLE
As you can see, myuser doesn't inherit the permissions of test but there is nothing to stop you switching directly to that role.
If you find this fiddly and confusing, then you are far from alone. I find it useful to add some tests to check any configuration I set up.
Because test is the owner of the database, it basically have all privileges on it on its subsequent objects.
As pointed out by #Richard Huxton setting NOINHERIT on test role prevent it to inherit form other roles, but does not prevent its privileges to be inherited to another roles.
When you issue:
GRANT test TO myuser;
You just grant the same privileges as the owner to myuser, therefore myuser can create object, actually it can do whatever the owner can.
It is not an issue of PostgreSQL it is the way ownership works. Anyway you can explicitly revoke privileges from owner (eg. not destroying an important object by mistake).
But you should consider other Privileges Policies for the role myuser, making it not inheriting from the owner but granting what it needs only. If the user can create a table, it should be blessed with:
GRANT CREATE ON SCHEMA test TO myuser;
I understand you are bit disappointed with PostgreSQL Privileges Management, at the very begging it can seem hard to understand through the documentation. To better learn how it actually works, you should issue:
REVOKE ALL ON DATABASE test FROM public;
Then all roles will need explicit privileges to CONNECT and SCHEMA USAGE. You will then discover the dependencies between objects and privileges. Reading more and more the GRANT page should be enough even if it is succinct.
About the SET ROLE security "issue", it is limited to all roles the current role is member of:
The specified role_name must be a role that the current session user
is a member of. (If the session user is a superuser, any role can be
selected.)
Therefore an user cannot override its privileges, it just can endorse other identity it has been granted with.

Sql Server Schemas, one private area per person, but have read access to others

I have a requirement for a database that is effectively a "play" area for people creating stats data for themselves and shared with others.
I have a read only database full of core data. All users have read access.
I want a second "play" database where 'n' people can query the "core" database and create there own tables, Sp's etc. I want these users to have a schema each. I want everything they create to be added to their schema by default unless they specify it explicitly (ie [dbo]) in the script. I want them all to be able to collaborate and be able to read data (and look at sp's) from other users schema but not add objects or execute sps' in the others users schema.
These users use 2014 management studio to run these queries. They all authenticate using Integrated Security (windows).
Can this be done?
I tried setting default schema on a user but by default when they add a table it goes to [dbo] because the property grid in 2014MS defaults to [dbo] and you have to edit it. If they just enter "create table Table1" I want it to go into their schema
I tried making the user the owner of the schema. I tried setting public to have select access to these user schemas. But something is not right!
I would have thought this was a common setup where developers get their own schema within a single database. Or, is it always the case that separate DB's are used to achieve this? I am sure others would appreciate a short script that sets this up for a couple of users :)
SQL Server admin is not my main area, so any guidance would be appreciated
For a test user User1, you can create a schema UserSchema1 and do the following
I want a second "play" database where 'n' people can query the "core" database
you can make the user part of the db_datareader role like this
USE [CoreDatabase]
GO
exec sp_addrolemember db_datareader, 'User1'
I want everything they create to be added to their schema by default
Create the schema and add appropriate permissions
USE [PlayDatabase]
GO
CREATE SCHEMA [UserSchema1] AUTHORIZATION dbo;
GO
GRANT ALTER, DELETE, EXECUTE, INSERT, REFERENCES, SELECT, UPDATE, VIEW DEFINITION ON SCHEMA::UserSchema1 TO User1;
GRANT CREATE TABLE, CREATE PROCEDURE, CREATE FUNCTION, CREATE VIEW TO User1;
Make the schema default for the user
ALTER USER [User1] WITH DEFAULT_SCHEMA=[UserSchema1]
For more info refer this thread
https://dba.stackexchange.com/questions/21733/allow-user-to-do-anything-within-his-own-schema-but-not-create-or-drop-the-schem
I want them all to be able to collaborate and be able to read data (and look at sp's) from other users schema but not add objects or execute sps' in the others users schema.
Allow user to view definition of other user's objects and do SELECT's on play database
USE [PlayDatabase]
GO
GRANT VIEW Definition TO [User1]
GO
exec sp_addrolemember db_datareader, 'User1'

PostgreSQL - Securing DB and hide structure

I am deploying a database in postgreSQL and I created a user that just will be able to execute certain functions.
I revoked all privileges from the user i just created and granted connect privileges executing:
REVOKE ALL PRIVILEGES ON DATABASE <database> FROM my_user;
REVOKE ALL PRIVILEGES ON SCHEMA public TO my_user;
GRANT CONNECT ON DATABASE <database> TO my_user;
But when i connect to the database with this user, i am able to read all table structures and all function source codes. Is there a way to hide it from this user?
I take the chance to make another question: I want to just execute functions (which may include select, insert or update on database tables) with this user, but I don't want to grant privileges on select, update or delete on tables.
I am using "SECURITY DEFINER" and then I grant execution, but I think it may be a little insecure. Am I right? is there any other way to do it?
Thanks in Advance.
Lamis
There's no way to hide the system catalogues from a user in PostgreSQL. If a user can't access the catalogues then they can't locate any other database objects.
If you really can't afford to let them see the structure of the db, you'll need to prevent them connecting. Build some sort of middle layer with a simple API that calls the db.
SECURITY DEFINER is the standard way to provide limited access at a higher privilege level. You have to be careful with any function arguments that can end up in a dynamic query though. That's the same "bobby tables" issue as with any dynamic sql building though.
How about
REVOKE SELECT ON pg_namespace FROM my_user;
REVOKE SELECT ON pg_catalog.pg_database FROM my_user;
You won't be able to see anything, but you'll be able to make queries if you know the namespace and table name.

how to Prevent alter a database

What is the best way to prevent changes to a database or verify the integrity of this, so that it can not be altered from an application created for this database.
assuming you have a username and password to access the database permits reading - writing.
requirements:
The user has write permissions
Do not depend on a particular system like (MySQL, Oracle, SQL Server)
solution I'm looking for is not based on the user's permissions on the database
Most modern databases allow you to grant reading and writing permissions but while disallowing DDL commands like ALTER TABLE.
Do not give users that should not alter the DB structure permission to execute DDL.
If by "Alter" you mean change any data rows, rather than the database structure, you can grant the user only SELECT rights.
The user or account that your application uses must be granted permissions from the database server. Typically permissions include things like:
Select
Insert
Update
Delete
Alter
Drop
Only give the user account the permissions needed; in other words, don't grant Alter permission, and the application (or anyone using the same login) won't be able to alter tables.
Two strategies: 1) if you are running SQL Server, Oracle, DB2, etc, you can configure permissions so users are reader/writer by default (which means no alter permissions). 2) you can periodically check to see if someone has changed the data structure or even set up a DB trigger to detect changes and record who/when, etc (depends on your DB platform)

Resources