{"id":77598,"date":"2018-03-19T00:16:31","date_gmt":"2018-03-19T00:16:31","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=77598"},"modified":"2019-01-22T17:21:44","modified_gmt":"2019-01-22T17:21:44","slug":"securing-access-administrator","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/blogs\/securing-access-administrator\/","title":{"rendered":"Securing access to data for admin and dbo"},"content":{"rendered":"<p>In this blog, I want to explore what you can do to block the owner of a database from doing stuff in the database they &#8220;own&#8221;.\u00a0 Own is a strange term, because really there is just one user that is listed as owner, but there are there are three users who essentially are owner level, super-powered users in a database:<\/p>\n<p>1. A login using server rights, usually via the sysadmin server role (or a server permission to view all data)<br \/>\n 2. The user dbo in a database, acquired either as a sysadmin, or as being the user listed as the owner of the database<br \/>\n 3. Members of the db_owner database role<\/p>\n<p>Sometimes, in the context of a database, these all start to blur together. But they are definitely all three independent things. Let&#8217;s write some code and see the differences, and one of the cases may be surprising to you. To do this, I will just be using the SELECT permission on a single table, but other rights will generally behave similarly. Note that another tool in your toolbox is\u00a0<span style=\"float: none;background-color: transparent;color: #333333;cursor: text;font-family: Georgia,'Times New Roman','Bitstream Charter',Times,serif;font-size: 16px;font-style: normal;font-variant: normal;font-weight: 400;letter-spacing: normal;text-align: left;text-decoration: none;text-indent: 0px\">Row Level Security in SQL Server 2016+. It is different, in that you can include a predicate that excludes the dbo, which you can read more about <a href=\"http:\/\/sqlblog.com\/blogs\/louis_davidson\/archive\/tags\/Row+Level+Security\/default.aspx\">here<\/a> in some of my earlier blogs.<\/span><\/p>\n<p>For our tests, we will use the following security objects.\u00a0<\/p>\n<p>The sysadmin role is generally consider the super, ignore all other security user, and this is generally the case. Let&#8217;s start the example by\u00a0creating a server standard login, and make it a part of the sysadmin server role. I am using a standard loging to cover pretty much all typical cases, including how things work for you using a domain account in Windows Authentication, in Linux, etc. If you are working on a laptop where you log in using a hotmail\/outlook account to log in, things behave in an atypical manner, so the standard login is the simplest.<\/p>\n<p><code>--If you wish, you can use the user you are logged in as instead of the TypicalAdminLogin, but this works for any case<\/code><br \/>\n <code>CREATE LOGIN TypicalAdminLogin WITH PASSWORD = '33nqq0u2cnX$v3';\u00a0<\/code><br \/>\n <code>GO<\/code><br \/>\n <code>ALTER SERVER ROLE sysadmin ADD MEMBER TypicalAdminLogin;<\/code><br \/>\n <code>GO<\/code><\/p>\n<p>A second login will be used to show what happens when a user that is not the owner of the database, but is sysadmin tries to access the data. We will use this login to try out the server level permissions also<\/p>\n<p><code>CREATE LOGIN AnotherTypicalAdminLogin WITH PASSWORD = '33nqq0u2cnX$v3';<\/code><br \/>\n <code>GO<\/code><br \/>\n <code>ALTER SERVER ROLE sysadmin ADD MEMBER AnotherTypicalAdminLogin;<\/code><br \/>\n <code>GO<\/code><\/p>\n<p> If you wish, you can use the user you are logged in or disconnect and relogin as the TypicalAdminLogin login for the demonstration.<\/p>\n<p>Next create a new db, as that login, so you are logged in as the owner of the database:<\/p>\n<p> <code>CREATE DATABASE TestDatabaseOwnerSecurity;<\/code><br \/>\n <code>GO<\/code><br \/>\n <code>USE TestDatabaseOwnerSecurity;<\/code><br \/>\n <code>GO<\/code><\/p>\n<p>To test access, I will create a table named testRights, with a single test row, and will not grant any user SELECT privileges on this table:<\/p>\n<p><code>CREATE TABLE dbo.TestRights<\/code><br \/>\n <code>(<\/code><br \/>\n <code>\u00a0 \u00a0\u00a0 value varchar(20) CONSTRAINT PKTestRights PRIMARY KEY<\/code><br \/>\n <code>);<\/code><br \/>\n <code>INSERT INTO dbo.testRights (value)<\/code><br \/>\n <code>VALUES ('Can See'); <\/code><br \/>\n <code>GO<\/code><\/p>\n<p>To enable easy checking of status of each scenario, I will create the following procedure. It will give me the owner of the db, and the context of the user. I used EXECUTE with a literal to avoid the security aspects of stored procedures, so it is simply shorthand to not repeat a lot of code throughout the blog;<\/p>\n<p> <code>CREATE OR ALTER PROCEDURE dbo.Security$CheckStatus<\/code><br \/>\n <code>AS<\/code><br \/>\n <code>EXECUTE ('<\/code><code>SELECT SUSER_SNAME(owner_sid) AS DatabaseOwner,<\/code><br \/>\n <code>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0 SUSER_SNAME() AS ServerPrincipal,<\/code><br \/>\n <code>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0 USER_NAME() AS DBPrincipal,<\/code><br \/>\n <code>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0 IS_MEMBER(''db_owner'') AS memberDBO,<\/code><br \/>\n <code>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0 IS_SRVROLEMEMBER(''sysadmin'') AS memberSysAdmin<\/code><br \/>\n <code>FROM sys.databases<\/code><br \/>\n <code>WHERE name = ''testDatabaseOwnerSecurity'';<\/code><\/p>\n<p><code>SELECT value<\/code><br \/>\n <code>FROM dbo.TestRights;')<\/code><br \/>\n <code>GO<\/code><br \/>\n <code>GRANT EXECUTE ON dbo.Security$CheckStatus TO PUBLIC; --everyone should be able to execute this<\/code><\/p>\n<p>Now, let&#8217;s see the output when the user that created the database is :<\/p>\n<p> <code>EXECUTE dbo.Security$CheckStatus<\/code><\/p>\n<table style=\"height: 127px\" width=\"782\">\n<tbody>\n<tr>\n<td style=\"width: 167.2px\"><code><span style=\"text-decoration: underline\"><span style=\"color: #222222;font-family: Consolas;text-decoration: underline\">DatabaseOwner<\/span><\/span><\/code><\/td>\n<td style=\"width: 167.2px\"><code><span style=\"text-decoration: underline\"><span style=\"color: #222222;font-family: Consolas;text-decoration: underline\">ServerPrincipal\u00a0\u00a0\u00a0\u00a0 <\/span><\/span><\/code><\/td>\n<td style=\"width: 140.8px\"><code><span style=\"text-decoration: underline\"><span style=\"color: #222222;font-family: Consolas;text-decoration: underline\">DBPrincipal\u00a0\u00a0\u00a0 <\/span><\/span><\/code><\/td>\n<td style=\"width: 132px\"><code><span style=\"text-decoration: underline\"><span style=\"color: #222222;font-family: Consolas;text-decoration: underline\">memberDBO\u00a0\u00a0 <\/span><\/span><\/code><\/td>\n<td style=\"width: 140.8px\"><code><span style=\"text-decoration: underline\"><span style=\"color: #222222;font-family: Consolas;text-decoration: underline\">memberSysAdmin<\/span><\/span><\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"width: 167.2px\"><code><span style=\"color: #222222;font-family: Consolas\">TypicalAdminLogin\u00a0\u00a0 <\/span><\/code><\/td>\n<td style=\"width: 167.2px\"><code><span style=\"color: #222222;font-family: Consolas\">TypicalAdminLogin\u00a0\u00a0 <\/span><\/code><\/td>\n<td style=\"width: 140.8px\"><code><span style=\"color: #222222;font-family: Consolas\">dbo\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0 <\/span><\/code><\/td>\n<td style=\"width: 132px\"><code><span style=\"color: #222222;font-family: Consolas\">1<\/span><\/code><\/td>\n<td style=\"width: 140.8px\"><code><span style=\"color: #222222;font-family: Consolas\">1<\/span><\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"width: 167.2px\">\u00a0<\/td>\n<td style=\"width: 167.2px\">\u00a0<\/td>\n<td style=\"width: 140.8px\">\u00a0<\/td>\n<td style=\"width: 132px\">\u00a0<\/td>\n<td style=\"width: 140.8px\">\u00a0<\/td>\n<\/tr>\n<tr>\n<td style=\"width: 167.2px\"><code><span style=\"text-decoration: underline\"><span style=\"color: #222222;font-family: Consolas;text-decoration: underline\">value<\/span><\/span><\/code><\/td>\n<td style=\"width: 167.2px\">\u00a0<\/td>\n<td style=\"width: 140.8px\">\u00a0<\/td>\n<td style=\"width: 132px\">\u00a0<\/td>\n<td style=\"width: 140.8px\">\u00a0<\/td>\n<\/tr>\n<tr>\n<td style=\"width: 167.2px\"><code><span style=\"color: #222222;font-family: Consolas\">Can See<\/span><\/code><\/td>\n<td style=\"width: 167.2px\">\u00a0<\/td>\n<td style=\"width: 140.8px\">\u00a0<\/td>\n<td style=\"width: 132px\">\u00a0<\/td>\n<td style=\"width: 140.8px\">\u00a0<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>As expected. We have not granted any rights for this user to have access, so we know that we cannot REVOKE any rights to change anything. So applying a DENY is our only chance to remove the rights of the DBO. So let&#8217;s start by attempting to remove rights from the db_owner role:<\/p>\n<p> <code>DENY SELECT ON OBJECT::dbo.TestRights TO db_owner;<\/code><br \/>\n <code>DENY SELECT ON OBJECT::dbo.TestRights TO dbo;<\/code><\/p>\n<p>To which you are greeted with:<\/p>\n<p><code>Msg 4617, Level 16, State 1, Line 76<\/code><br \/>\n <code>Cannot grant, deny or revoke permissions to or from special roles.<\/code><\/p>\n<p><code>Cannot grant, deny, or revoke permissions to sa, dbo, entity owner, information_schema, sys, or yourself.<\/code><\/p>\n<p>The first is an error message, the next a message without error message number, but clearly, that did NOT work. So clearly we cannot revoke the rights from the group directly. What about indirectly? The error message may lean towards the following not working, but let&#8217;t DENY access to the object to the public role, since everyone is a member of this role and it will be simpler. <span style=\"float: none;background-color: transparent;color: #333333;cursor: text;font-family: Georgia,'Times New Roman','Bitstream Charter',Times,serif;font-size: 16px;font-style: normal;font-variant: normal;font-weight: 400;letter-spacing: normal;text-align: left;text-decoration: none;text-indent: 0px\">Doesn&#8217;t have to be public, it can be any other group that the users you are tryi<\/span>ng to DENY are a member of<\/p>\n<p><code>DENY SELECT ON OBJECT::dbo.TestRights TO public;\u00a0<\/code><\/p>\n<p>Ok, so what is this going to do? More than you may expect, actually. Clearly no user who is not one of the special roles is going to have access. But what about the three cases we have set up?<\/p>\n<p>1. Members of a server role<br \/>\n 2. The user dbo in a database<br \/>\n 3. Members of the db_owner database role<\/p>\n<p><strong>Members of a server role<\/strong><\/p>\n<p>The obvious example, which we will start on is the sysadmin of the server. Later, we will also see how the SELECT ALL USER SECURABLES permission works.<\/p>\n<p>Let&#8217;s try in our current context as the sysadmin\/owner of the database<\/p>\n<p> <code>EXECUTE dbo.Security$CheckStatus;<\/code><\/p>\n<p>Perhaps expected, the user can see still the data as they are dbo, sysadmin, and dbowner role.<\/p>\n<table width=\"782\">\n<tbody>\n<tr>\n<td><span style=\"text-decoration: underline\"><code>DatabaseOwner<\/code><\/span><\/td>\n<td><span style=\"text-decoration: underline\"><code>ServerPrincipal\u00a0\u00a0\u00a0\u00a0 <\/code><\/span><\/td>\n<td><span style=\"text-decoration: underline\"><code>DBPrincipal\u00a0\u00a0\u00a0 <\/code><\/span><\/td>\n<td><span style=\"text-decoration: underline\"><code>memberDBO\u00a0\u00a0 <\/code><\/span><\/td>\n<td><span style=\"text-decoration: underline\"><code>memberSysAdmin<\/code><\/span><\/td>\n<\/tr>\n<tr>\n<td><code>TypicalAdminLogin\u00a0\u00a0 <\/code><\/td>\n<td><code>TypicalAdminLogin\u00a0\u00a0 <\/code><\/td>\n<td><code>dbo\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0 <\/code><\/td>\n<td><code>1<\/code><\/td>\n<td><code>1<\/code><\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td><span style=\"text-decoration: underline\"><code>value<\/code><\/span><\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td><code>Can See<\/code><\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&nbsp;<\/p>\n<p>Now, let&#8217;s try as the admin that we created that is not the owner of the database<\/p>\n<p><code>EXECUTE AS LOGIN = 'AnotherTypicalAdminLogin';<\/code><br \/>\n <code>GO<\/code><br \/>\n <code>EXECUTE dbo.Security$CheckStatus;<\/code><br \/>\n <code>GO<\/code><br \/>\n <code>REVERT;<\/code><\/p>\n<p>This returns:<\/p>\n<table width=\"782\">\n<tbody>\n<tr>\n<td><span style=\"text-decoration: underline\"><code>DatabaseOwner<\/code><\/span><\/td>\n<td><span style=\"text-decoration: underline\"><code>ServerPrincipal\u00a0\u00a0\u00a0\u00a0 <\/code><\/span><\/td>\n<td><span style=\"text-decoration: underline\"><code>DBPrincipal\u00a0\u00a0\u00a0 <\/code><\/span><\/td>\n<td><span style=\"text-decoration: underline\"><code>memberDBO\u00a0\u00a0 <\/code><\/span><\/td>\n<td><span style=\"text-decoration: underline\"><code>memberSysAdmin<\/code><\/span><\/td>\n<\/tr>\n<tr>\n<td><code>TypicalAdminLogin\u00a0\u00a0 <\/code><\/td>\n<td><code><span style=\"float: none;background-color: transparent;color: #333333;cursor: text;font-family: Georgia,'Times New Roman','Bitstream Charter',Times,serif;font-size: 16px;font-style: normal;font-variant: normal;font-weight: 400;letter-spacing: normal;text-align: left;text-decoration: none;text-indent: 0px\">AnotherTypicalAdminLogin <\/span><\/code><\/td>\n<td><code>dbo\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0 <\/code><\/td>\n<td><code>1<\/code><\/td>\n<td><code>1<\/code><\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td><span style=\"text-decoration: underline\"><code>value<\/code><\/span><\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td><code>Can See<\/code><\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&nbsp;<\/p>\n<p>Expected. But now, let&#8217;s try creating a user for that login in the database, and use DENY to try to stop access:<\/p>\n<p><code>CREATE USER AnotherTypicalAdminLogin FOR LOGIN AnotherTypicalAdminLogin;<\/code><br \/>\n <code>DENY SELECT ON OBJECT::dbo.TestRights TO AnotherTypicalAdminLogin;<\/code><br \/>\n <code>GO<\/code><\/p>\n<p> Execute the Security$CheckStatus procedure as before, and you will see the exact same results, so sysadmin is sysadmin, no matter any users in the database. If you remove the sysadmin server role from the server user.<\/p>\n<p> <code>ALTER SERVER ROLE sysadmin DROP MEMBER AnotherTypicalAdminLogin;<\/code><\/p>\n<p> Then this would be a completely different setup, with this login having access to the database from their user (which is automatically granted the CONNECT database right), and you will see things change to no access to the table, and not a member of any special role:<\/p>\n<table width=\"782\">\n<tbody>\n<tr>\n<td><span style=\"text-decoration: underline\"><code>DatabaseOwner<\/code><\/span><\/td>\n<td><span style=\"text-decoration: underline\"><code>ServerPrincipal\u00a0\u00a0\u00a0\u00a0 <\/code><\/span><\/td>\n<td><span style=\"text-decoration: underline\"><code>DBPrincipal\u00a0\u00a0\u00a0 <\/code><\/span><\/td>\n<td><span style=\"text-decoration: underline\"><code>memberDBO\u00a0\u00a0 <\/code><\/span><\/td>\n<td><span style=\"text-decoration: underline\"><code>memberSysAdmin<\/code><\/span><\/td>\n<\/tr>\n<tr>\n<td><code>TypicalAdminLogin\u00a0\u00a0 <\/code><\/td>\n<td><code>AnotherTypicalAdminLogin <\/code><\/td>\n<td><code>AnotherTypicalAdminLogin<\/code><\/td>\n<td><code>0<\/code><\/td>\n<td><code>0<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&nbsp;<\/p>\n<p><code>Msg 229, Level 14, State 5, Line 141<\/code><br \/>\n <code>The SELECT permission was denied on the object 'testRights', database 'TestDatabaseOwnerSecurity', schema 'dbo'.<\/code><\/p>\n<p>So sysadmin is not affected by a DENY, but what about other server security? For example, using the following 3 GRANT statements, we can configure the user to have access to all of the data on a server, but will the DENY affect this?<\/p>\n<p> <code>USE Master;<\/code><br \/>\n <code>--These rights are an awesome way to create a read-only admin role<\/code><br \/>\n <code>GRANT VIEW ANY DATABASE to AnotherTypicalAdminLogin; --see any database<\/code><br \/>\n <code>GRANT CONNECT ANY DATABASE to AnotherTypicalAdminLogin; --set context to any database<\/code><br \/>\n <code>GRANT SELECT ALL USER SECURABLES to AnotherTypicalAdminLogin; --see any data in databases <\/code><br \/>\n <code>GO<\/code><br \/>\n <code>USE TestDatabaseOwnerSecurity;<\/code><\/p>\n<p>Now trying to access the data again:<\/p>\n<p><code>EXECUTE AS LOGIN = 'AnotherTypicalAdminLogin'<\/code><br \/>\n <code>GO<\/code><br \/>\n <code>EXECUTE dbo.Security$CheckStatus<\/code><br \/>\n <code>GO<\/code><br \/>\n <code>REVERT;<\/code><\/p>\n<p>And you will see the very same failure as before, which is exactly what you probably desire. Hence, these rights do NOT override local rights. Th<span style=\"float: none;background-color: transparent;color: #333333;cursor: text;font-family: Georgia,'Times New Roman','Bitstream Charter',Times,serif;font-size: 16px;font-style: normal;font-variant: normal;font-weight: 400;letter-spacing: normal;text-align: left;text-decoration: none;text-indent: 0px\">is is definitely a very fringe case to configure, because it would be very rare to DENY data to public.. so you would need the login to have a user in the database to DENY them, but it is what it is.<\/span><\/p>\n<p>Removing the DENY from the USER, and even dropping the user will not change anything:<\/p>\n<p><code>REVOKE SELECT ON OBJECT::dbo.TestRights TO AnotherTypicalAdminLogin;<\/code><br \/>\n <code>DROP USER AnotherTypicalAdminLogin;<\/code><\/p>\n<p>Only if you were to REVOKE the deny to public will the AnotherTypicalAdminLogin (now a server login only if you try both of those two statements), will then open up rights<\/p>\n<p> <code>REVOKE SELECT ON OBJECT::dbo.TestRights TO public;<\/code><br \/>\n <code>GO<\/code><br \/>\n <code>EXECUTE AS LOGIN = 'AnotherTypicalAdminLogin';<\/code><br \/>\n <code>GO<\/code><br \/>\n <code>EXECUTE dbo.Security$CheckStatus;<\/code><br \/>\n <code>GO<\/code><br \/>\n <code>REVERT;<\/code><br \/>\n <code>GO<\/code><\/p>\n<p> This now allows the user to execute the code:<\/p>\n<table width=\"782\">\n<tbody>\n<tr>\n<td><span style=\"text-decoration: underline\"><code>DatabaseOwner<\/code><\/span><\/td>\n<td><span style=\"text-decoration: underline\"><code>ServerPrincipal\u00a0\u00a0\u00a0\u00a0 <\/code><\/span><\/td>\n<td><span style=\"text-decoration: underline\"><code>DBPrincipal\u00a0\u00a0\u00a0 <\/code><\/span><\/td>\n<td><span style=\"text-decoration: underline\"><code>memberDBO\u00a0\u00a0 <\/code><\/span><\/td>\n<td><span style=\"text-decoration: underline\"><code>memberSysAdmin<\/code><\/span><\/td>\n<\/tr>\n<tr>\n<td><code>TypicalAdminLogin\u00a0\u00a0 <\/code><\/td>\n<td><code>AnotherTypicalAdminLogin <\/code><\/td>\n<td><code>AnotherTypicalAdminLogin<\/code><\/td>\n<td><code>0<\/code><\/td>\n<td><code>0<\/code><\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td><span style=\"text-decoration: underline\"><code>value<\/code><\/span><\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td><code>Can See<\/code><\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&nbsp;<\/p>\n<p>This is really great news for the system administrator. Using server rights, you can give a login access to either:<\/p>\n<p>* ALL data on the server as a sysadmin<br \/>\n * All un-denied data on the server using SELECT ALL USER SECURABLES and rights to connect to a database.<\/p>\n<p><code>--put the DENY back for the rest of the examples<\/code><br \/>\n <code>DENY SELECT ON OBJECT::dbo.TestRights TO public;<\/code><\/p>\n<p><strong>The user dbo in a database.<\/strong><\/p>\n<p>The next case we will try is when a user is mapped to the dbo user, specifically. We have already shown that we can&#8217;t stop the sysadmin, nor can we deny the dbo specifically. Now, lets attempt to add a login, make it the owner of the db, but no other rights in the database or server, other than ownership of the database:<\/p>\n<p> <code>CREATE LOGIN NonAdminLogin WITH PASSWORD = '33nqq0u2cnX$v3';<\/code><br \/>\n <code>ALTER AUTHORIZATION ON DATABASE::TestDatabaseOwnerSecurity TO NonAdminLogin;<\/code><br \/>\n <code>GO<\/code><\/p>\n<p>Now try to access the date using this login, that is the database owner:<\/p>\n<p> <code>EXECUTE AS LOGIN = 'NonAdminLogin'<\/code><br \/>\n <code>GO<\/code><br \/>\n <code>EXECUTE dbo.Security$CheckStatus<\/code><br \/>\n <code>GO<\/code><br \/>\n <code>REVERT;<\/code><br \/>\n <code>GO<\/code><\/p>\n<p><code><\/code>This user is translated, as are sysadmin role logins, to the dbo user in the database, and as such, has full access:<\/p>\n<table width=\"782\">\n<tbody>\n<tr>\n<td><span style=\"text-decoration: underline\"><code>DatabaseOwner<\/code><\/span><\/td>\n<td><span style=\"text-decoration: underline\"><code>ServerPrincipal\u00a0\u00a0\u00a0\u00a0 <\/code><\/span><\/td>\n<td><span style=\"text-decoration: underline\"><code>DBPrincipal\u00a0\u00a0\u00a0 <\/code><\/span><\/td>\n<td><span style=\"text-decoration: underline\"><code>memberDBO\u00a0\u00a0 <\/code><\/span><\/td>\n<td><span style=\"text-decoration: underline\"><code>memberSysAdmin<\/code><\/span><\/td>\n<\/tr>\n<tr>\n<td><code>NonAdminLogin\u00a0<\/code><\/td>\n<td><code>NonAdminLogin<\/code><\/td>\n<td><code>dbo<\/code><\/td>\n<td><code>0<\/code><\/td>\n<td><code>0<\/code><\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td><span style=\"text-decoration: underline\"><code>value<\/code><\/span><\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td><code>Can See<\/code><\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&nbsp;<\/p>\n<p>So the user dbo cannot be denied, either by statement, or by public.<\/p>\n<p><strong>Members of the db_owner database role<\/strong><\/p>\n<p>Finally, let&#8217;s try one more thing, that intuitively, seems unnecessary to try. I know, at least by name, I expected that <em><strong>&#8220;member of db_owner database role&#8221;<\/strong><\/em> = <em><strong>&#8220;dbo&#8221;<\/strong><\/em>. But&#8230; is that true? Created a user named TestDbo in the datbase and make it a member of the db_owner role . It has no login to get security from, so this is the only right applied.<\/p>\n<p> <code>CREATE USER TestDbo WITHOUT LOGIN;<\/code><\/p>\n<p><code>ALTER ROLE db_owner ADD MEMBER TestDbo;<\/code><br \/>\n <code>GO<\/code><\/p>\n<p>Executing the query, you get probably, NOT what you expect (I didn&#8217;t!):<\/p>\n<p> <code>EXECUTE AS User = 'TestDbo'<\/code><br \/>\n <code>GO<\/code><br \/>\n <code>EXECUTE dbo.Security$CheckStatus<\/code><br \/>\n <code>GO<\/code><br \/>\n <code>REVERT;<\/code><br \/>\n <code>GO<\/code><\/p>\n<table width=\"782\">\n<tbody>\n<tr>\n<td><span style=\"text-decoration: underline\"><code>DatabaseOwner<\/code><\/span><\/td>\n<td><span style=\"text-decoration: underline\"><code>ServerPrincipal\u00a0\u00a0\u00a0\u00a0 <\/code><\/span><\/td>\n<td><span style=\"text-decoration: underline\"><code>DBPrincipal\u00a0\u00a0\u00a0 <\/code><\/span><\/td>\n<td><span style=\"text-decoration: underline\"><code>memberDBO\u00a0\u00a0 <\/code><\/span><\/td>\n<td><span style=\"text-decoration: underline\"><code>memberSysAdmin<\/code><\/span><\/td>\n<\/tr>\n<tr>\n<td><code>NonAdminLogin\u00a0<\/code><\/td>\n<td><code>S-1-9-3-2245412934-107...<\/code><\/td>\n<td><code>testDbo<\/code><\/td>\n<td>1<\/td>\n<td><code>0<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><code>Msg 229, Level 14, State 5, Line 282<\/code><br \/>\n <code>The SELECT permission was denied on the object 'testRights', database 'TestDatabaseOwnerSecurity', schema 'dbo'.<\/code><\/p>\n<p>Membership in the db_owner role is not exactly the same as being the dbo. The dbouser, like sysadmin server role members, is not subject to any &#8220;typical&#8221; security, but role members are.\u00a0<\/p>\n<p><strong>Summary<\/strong><\/p>\n<p>Security is a MESSY subject, and can be difficult to configure correctly. Users with too much security are very dangerous to the health and accessibility of your data. Too little and people are annoyed, but too much and you may end up sharing more to the user than expected.<\/p>\n<p>Here we have covered the ways that you can affect a user&#8217;s access to data in several ways (Ignoring <span style=\"float: none;background-color: transparent;color: #333333;cursor: text;font-family: Georgia,'Times New Roman','Bitstream Charter',Times,serif;font-size: 16px;font-style: normal;font-variant: normal;font-weight: 400;letter-spacing: normal;text-align: left;text-decoration: none;text-indent: 0px\">Row Level Security, which can also be useful)<\/span>:<\/p>\n<p>Using server rights and roles. Sysadmin logins are all powerful, but using the SELECT ALL USER SECURABLES server permission can be counteracted by using DENY at the database level.<\/p>\n<p>The user dbo in a database is all powerful, but members of the db_owner database role are not equivalent to the dbo user. These users are subject to a DENY, unlike the DBO.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this blog, I want to explore what you can do to block the owner of a database from doing stuff in the database they &#8220;own&#8221;.\u00a0 Own is a strange term, because really there is just one user that is listed as owner, but there are there are three users who essentially are owner level,&#8230;&hellip;<\/p>\n","protected":false},"author":56085,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[2],"tags":[4619,68855,4183],"coauthors":[19684],"class_list":["post-77598","post","type-post","status-publish","format-standard","hentry","category-blogs","tag-security","tag-sql-provision","tag-t-sql"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/77598","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/56085"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=77598"}],"version-history":[{"count":7,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/77598\/revisions"}],"predecessor-version":[{"id":77605,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/77598\/revisions\/77605"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=77598"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=77598"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=77598"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=77598"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}