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.
Related
I really need some advice about adding roles at the server level and apply some permissions that will be applicable to each database on my server.
Basically I need to two roles read only and read write.
The read will have permissions to select and see any object
The write will have permissions to select/insert/delete and execute any object
I want to create a server role, then a login associated to this role (which can be a AD group) and after that for each database create a user that will inherits all permissions from the server role.
So on each database, I will have each user that belongs to the server role created, the problem is to clearly define the permissions, is not straight forward in my opinion.
What I can see, I cannot assign read or write to a role and then use it on each db, on the contrary on the General tab of the server role I have a bunch of permissions that is not clear which one to use for this purpose.
Maybe I'm doing it wrong but I want to have something at the server level and not define the same role on each db for that purpose. I'm using SQL server 2014.
The short answer is you can't.
Generally, server-level permissions are not propagated down to individual objects within databases. The only exception is a sysadmin role, which I would strongly encourage you not to use for this purpose, as you would essentially give up the control of the entire server instance to every member of it.
As a kind of a shorthand, you can use built-in database roles to save yourself a bit of trouble. For read-only access, a membership in db_datareader role is usually enough, unless you have stored procedures that return datasets which this role is supposed to be able to execute. There is also a similar role for modification, db_datawriter, but it doesn't cover the execute permission. So you will have to create a custom role for that:
create role [DataChanger] authorization [dbo];
go
alter role [db_datareader] add member [DataChanger];
go
alter role [db_datawriter] add member [DataChanger];
go
grant execute to [DataChanger];
go
-- Now you can add your members. Here is a reader
create user [Domain\MyUser1] from login [Domain\MyUser1];
go
alter role [db_datareader] add member [Domain\MyUser1];
go
-- Writer
create user [Domain\MyUser2] from login [Domain\MyUser2];
go
alter role [DataChanger] add member [Domain\MyUser2];
go
These permissions will automatically pick up newly created objects, without you having to explicitly add new permissions after every schema modification.
You will have to do this in the context of every user database that you want to manage in this way. You can probably create a SQL Agent job which will run periodically and introduce these changes in any user databases which don't have them already (for example, if a database has been restored from earlier backup, or brought from another server, or a new one was created). Also, since you can't loop through databases in static code, you will need to wrap it into a dynamic SQL and loop through sys.databases, or maybe via an undocumented sp_MSforeachdb system stored procedure. Oh, and don't forget to remove all these go statements from dynamic code, as they are not part of SQL, and are only recognised by SSMS and sqlcmd.
P.S. All that being said, I hope you are not going to manage any production databases in this manner. I don't even know where to start on how insecure this approach is.
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;
How do I replace the ALL permission now that it has been revoked:
REVOKE ALL ON dbo.MyObject TO MyUser
I'm looping through all the objects in a database and revoking all permissions for a specific user. So if I had to be specific about which permission to revoke, it would be a real hassle because I would have to find out what object I have and then revoke every possible permission for that type of object. Instead of just REVOKE ALL.
You can't revoke all on schema::dbo to your user but you can...
REVOKE select,update,delete,insert,execute,references on schema::DBO to USER
If you want to get a list of securables to which a database principal has been granted permission, look no further than sys.database_permissions. How to interpret the major_id and minor_id columns depends on the value of the class column (for instance, if class = 1, then major_id = object_id and minor_id = column_id). I leave that as an exercise to the reader (check out the BOL entry for it here: http://msdn.microsoft.com/en-us/library/ms187719.aspx).
Another option might be just to drop and re-add the principal. Dropping the principal would get rid of any permissions associated directly to it and re-adding it doesn't grant any (unless you specifically grant some). That might be another option (but test it first!).
You really need to have some idea what permissions you want the user to have. the pattern you have there is "Just revoke everything, then later put back whatever was there before...".
If you really need to do that, then you will have to keep your own record of what the permissions were to begin with.
A better solution would be to have some idea what the permissions ought to be. Who knows? They may have been wrong before!
Is there a way I can give developers permission to grant a user permissions over objects without giving them the option to create users or functions?
I'm trying to limit developers permissions, I recently found out that developers had db_owner permissions in dev and prod environments! So I'm doing my best to stop this madness.
Any good article about this matter?
You can make them members of the "db_securityadmin" database role
As said, if someone could hand out permissions, they could hand out permissions to themselves (or a dummy account). I'm not sure if there is a trick in SQL Server to provide "give user permissions less then me".
The way I would do it is with stored procedures.
Create a stored procedure that gives a specified user a specific right or set of rights (those rights are the ones that regular users are allowed to have). Then give the developers execute access to this stored procedure. In effect you use stored procedures to create a limited version of GRANT, while keeping the full GRANT command to yourself.
If someone can give someone else permissions, he can also give himself the permission to do what he wants. So what is this good for? Probably I don't understand your situation.
Owners of objects can grant permissions on those objects. Provided your developers don't need to grant things like CREATE TABLE rights, you might be able to give them ownership of the objects that you want them to grant permission on.
As Stefan said, giving them grant permissions would effectively give them all permissions, since if they want to do something all they have to do is grant themselves the permissions to do it.
Rather than considering the developers the enemy, though, you may want to consider giving the developers a second user account that's used to administer the database. It's pretty common not to give developers ANY permissions to production, at least on their development account.
Setting permission on objects like stored procedures can be accomplished with "GRANT EXECUTE ON . to ;
However, you may also want to grant security rights at both the login and user level. You will want to determine and grant ONLY the necessary rights for the objects that require access (such as execution). Consider use of the "EXECUTE AS" capability which enables impersonation of another user to validate permissions that are required to execute the code WITHOUT having to grant all of the necessary rights to all of the underlying objects (e.g. tables). The EXECUTE AS can be added to stored procs, functions, triggers, etc.
Add to the code as follows right within the Stored Procedure: CREATE PROCEDURE dbo.MyProcedure WITH EXECUTE AS OWNER
In this case you are impersonating the owner of the module being called. You can also impersonate SELF, OR the user creating or altering the module OR... imperonate CALLER , which will enable to module to take on the permissionsof the current user, OR... impersonate OWNER, which will take on the permission of the owner of the procedure being called OR... impersonate 'user_name', which will impersonate a specific user OR... impersonate 'login_name' with will impersonate a specific login.
MOST of the time, you will only need to grant EXECUTE rights to stored procs and then rights are granted to all objects referenced within the stored proc.
In this way, you DO NO need to give implicit rights (example: to update data or call additional procs). Ownership chaining handles this for you. This is especially helpful for dynamic sql or if you need to create elevated security tasks such as CREATE TABLE. EXECUTE AS is a handy tool to consider for these.
This example may help clarify all of this:
Create a user called NoPrivUser with public access to a database (e.g. dbadb)
USE [master] GO CREATE LOGIN [NoPrivUser] WITH PASSWORD=N'ABC5%', DEFAULT_DATABASE=[dbadb], CHECK_EXPIRATION=ON, CHECK_POLICY=ON GO USE [DBAdb] GO CREATE USER [NoPrivUser] FOR LOGIN [NoPrivUser] GO
NOTE: CREATOR OR OWNER OF THIS PROCEDURE WILL REQUIRE CREATE TABLE RIGHTS within the target database.
use DBAdb go CREATE PROCEDURE dbo.MyProcedure WITH EXECUTE AS OWNER AS IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].MyTable') AND type in (N'U')) CREATE TABLE MyTable (PKid int, column1 char(10)) INSERT INTO MyTable VALUES (1,'ABCDEF')
GO
GRANT EXEC ON dbo.MyProcedure TO NoPrivUser; GO
-- Now log into your database server as NoPrivUser and run the following.
use dbadb go
EXEC dbo.MyProcedure
(1 row(s) affected)
Now try to select from the new table while logged on as NoPrivuser.
You will get the following:
select * from MyTable go
Msg 229, Level 14, State 5, Line 1 The SELECT permission was denied on the object 'MyTable', database 'DBAdb', schema 'dbo'.
That is expected since you only ran the procedure under the security context of Owner while logged on as NoPrivUser.
NoPrivUser as no rights to actually read the table. Just to execute the procedure which creates and inserts the rows.
With the EXECUTE AS clause the stored procedure is run under the context of the object owner. This code successfully creates dbo.MyTable and rows are inserted successfully. In this example, the user "NoPrivUser" has absolutey no granted rights to modify the table, or read or modify any of the data in this table.
It only takes on the rights needed to complete this specific task coded WITHIN the context of this procedure.
This method of creating stored procedures that can perform tasks that require elevated security rights without permanently assigning those rights come be very useful.
I've found that the most dangerous aspect of the db_owner role is that if you issue a deny on a permissions, then the members of the role can grant it back to themselves. I've just started reading about this and I'm testing this
Create role db_ControlDatabase
grant control to db_ControlDatabase
deny backup database to db_ControleDatabase
alter role db_ControlDatabase add member TestUser
So far, I've found that the subject TestUser has permissions without being able to add or remove members of the fixed database roles. You should be able to deny whatever you need at this point like backup certificate, backup master key, etc.
Here is a list of permissions that can be denied or granted:
I am trying to create a SQL server login and database user from within my application, along with a custom application user row. I want these users to be able to create other users - i.e. the application will control who can/can't create users but I need all users to have permissions for creating SQL server logins and database users.
I have got the server login permissions working - i.e. an existing user/login can create a new login - by adding the logins to the 'securityadmin' server role - which grants the 'ALTER ANY LOGIN' privilege.
I tried to do the same with the database users - adding them to the 'db_accessadmin' database role - which supposedly grants the ALTER ANY USER privilege, which is required for CREATE USER.
However any time I try to create a new database user using a user with the above privileges I get a permissions exception.
I have tried manually granting the ALTER ANY USER permission to a particular user (GRANT ALTER ANY USER TO demouser) but this doesn't work either.
Technically, yes. Whether it's right or wrong... no comment.
Anyway, database security is split into 2 functions:
db_accessadmin to manage users (or "ALTER ANY USER" permission as you mentioned)
db_securityadmin allows you to manage roles memberships and object permissions (or "ALTER ANY ROLE permission)
This is mentioned for sp_addrolemember.
You are actually changing the role, not the user, by running sp_addrolemember so "ALTER ANY ROLE" is enough without having full db_owner rights.
My bad - I have found the issue - it was not the CREATE USER that was failing, but a subsequent call to 'sp_addrolemember'. This requires further permissions that I wasn't assigning.
In particular I needed to add my users to the db_owner database role in order to allow them to assign other/new users to fixed database roles.
Is there a cleaner way to allow me to achieve what I am trying to do here - i.e. create users that are allowed to create other users?
This seems very dangerous, easily becoming a security nightmare. Not knowing anything about why you think this is the best solution to accomplish your objective I can't really tell you not to do it this way, but wow!! - I would think long and hard about whether this really is necessary. The spider-webbing of users just seems like it could quickly be impossible to manage from a DBA perspective.
Would you not be able to just have one SQL account that has the permissions to add users, and the application uses that every time to add new users? Those users then would not need the ability to add other users. Maybe this won't work for your specific objective, but surely there is some other way.
But having said all that ... no, there is not really a cleaner way. The user would have to be assigned to the correct roles in order to have the ability to later add other users.
/*
TOPIC: create a login ,who can add other logins to databases (securityadmin server role)
*/
USE MASTER
GO
Create login securityTestLogin with password = '##somepassword123'
-----add this to server , this is server level security role -------
EXEC master..sp_addsrvrolemember #loginame = N'securityTestLogin', #rolename = N'securityadmin'
--- first this login should be a user in database where we want to give other users access
USE HTDBA
GO
Create user securityTestLogin for login securityTestLogin
EXEC sp_addrolemember N'db_accessadmin', N'securityTestLogin'
-- depends on your requriemtnt you might also want this permission too
--EXEC sp_addrolemember N'db_securityadmin', N'securityTestLogin'
GO
------ Now we think about adding other users to different database roles -------------
/*
There is one gottcha , db_securityadmin role cannot add users to the fixed database roles ,only
db_owner can perform this action , but for security we don't want to give this permission .
so we need a work around
Create a role with required permission and then add users to that role.
*/
--Create user defined database role Readers
EXEC sp_addrole DBUser
-- Add this role to fixeddbroles to get database level permission
EXEC sp_addrolemember db_datareader, DBUser
EXEC sp_addrolemember db_datawriter, DBUser
GO
--------READY TO TEST --------
------ we are using this sample login for test
use master
Go
Create login testlogin1 with password='##somepassword123'
use HTDBA
go
Create user testlogin1 for login testlogin1
--- now add this user to user created DBUser role .
EXEC sp_addrolemember DBUser, testlogin1
A very good article on SQL permissions:
http://www.sqlservercentral.com/articles/Security/sqlserversecurityfixeddatabaseroles/1231/