By "database owner", here, I mean the principal whose SID is sys.databases
.owner_sid
.
This answer to the question of best practices for database ownership includes the following:
The answer here comes down to priorities: Convenience xor Security? Pick one.
One issue is that the database owner is used to determine permissions for certain operations. The sa account, by its very nature, is not restricted from anything (outside of maybe placing restrictions on itself, but now we're just being silly). I'm not sure what that list of operations is, but using a low-privileged SQL Server login as the owner is definitely one way that people discover what those operations are 😉 (hence why so many people prefer to use sa, even if it is a poor choice; more on that in a moment).
This suggests that the permissions granted to the database owner are factored into permissions checks during operations invoked in the security context of other principals. This sounds reasonable and I can imagine a circumstance where changing the database ownership resolved some permissions issue, but I have not seen that play out first-hand.
This answer seems to support this suggestion:
the most important thing: the database owner has important side effects, specifically the EXECUTE AS context. This later problem is what burns most users. Since Service Broker makes extensive use of EXECUTE AS (the message delivery has an implicit EXECUTE AS context, as well as queue activation that has an explicit one) is usually Service Broker users that discover this problem first.
This suggests that EXECUTE AS context
could lead to unintended escalation of privileges to those of the database owner.
Microsoft's "Summary of the Permission Check Algorithm" does not, however, seem to mentioned the database owner:
Security context
This is the group of principals that contribute permissions to the access check. These are permissions that are related to the current login or user, unless the security context was changed to another login or user by using the EXECUTE AS statement. The security context includes the following principals:
- The login
- The user
- Role memberships
- Windows group memberships
- If module signing is being used, any login or user account for the certificate used to sign the module that the user is currently executing, and the associated role memberships of that principal.
Microsoft's Chart of SQL Server Permissions also doesn't seem to mention owner_sid
(although a lot seems to hinge on the db_owner role, but that is a different matter).
Furthermore, Microsoft's "Understanding Execution Context" does not seem to make any special mention of the database owner.
This raises the following questions:
- What permissions, if any, hinge on
sys.databases.owner_sid
? - Are there operations that SQL Server invokes using
sys.databases.owner_sid
to which the "SQL Server Permission Check Algorithm" does not apply? - Does
EXECUTE AS ... context
somehow allow a normal user to inherit the permissions ofsys.databases.owner_sid
?
Best Answer
I had meant to write a blog post where I explain this in detail (more than is appropriate here), with example scripts, etc, but just haven't had the time. But, rather than have this remain unanswered until I have time to write that post (test scripts are done but need to be cleaned up), I will post the basic info here and come back later to adjust once that post is published.
There are two related scenarios that I know of that are dependent on the permissions of the database owner:
Trying to access instance-level resources, or another database, while using either impersonation or an App Role
Trying to obtain either
EXTERNAL ACCESS ASSEMBLY
orUNSAFE ASSEMBLY
permission (instance-level permissions) while using unsigned SQLCLR assemblies (and using either SQL Server 2005 - 2016 or SQL Server >= 2017 and not using the horrible "trusted assemblies" hack)Both scenarios are related by being times when enabling
TRUSTWORTHY
is required. And, both are also scenarios that can avoid the evilTRUSTWORTHY ON
by implementing the far better approach of Module Signing.Scenario #1
is very similar to, but still not the same as, actually accessing instance-level resources or another database under impersonation. There is a lot of overlap as they are two parts of the same functionality. I would guess that most of the time impersonation is being used to become the
dbo
user via theEXECUTE AS
clause of aCREATE {module}
statement, specifying eitherOWNER
orN'dbo'
. In that case it's very difficult to separate the indirect requirement, but impersonating a non-dbo
user makes it clear.When impersonating a user (we're assuming a non-
dbo
user), in order to access instance-level resources or another database, not only does the current database need to be marked asTRUSTWORTHY
, but the login associated with thedbo
user of the current database (i.e. same SID between the login anddbo
) needs to have theAUTHENTICATE SERVER
permission. This is not well known, which is likely why so many people simply resort to havingsa
own the database: it has all permissions. Many people, I'm sure, who already have their DBs owned bysa
might never even know that this is a potential problem because the condition was already satisfied when they enabledTRUSTWORTHY
in order to get the impersonation working.EXAMPLE:
Setup:
[TestDB]
, that hasTRUSTWORTHY
set toON
.[TestDB]
is owned by loginO
(this means that thedbo
user in[TestDB]
has the same SID as loginO
).O
is not a member ofsysadmin
nor hasCONTROL SERVER
permission, or any high-level permission.A
that has an associated userA
in database[TestDB]
. LoginA
is not a member ofsysadmin
nor hasCONTROL SERVER
permission, or any high-level permission.P
, that has an associated userP
in database[TestDB]
. LoginP
is not a member ofsysadmin
nor hasCONTROL SERVER
, but does have theVIEW SERVER STATE
permission.[TestDB]
nameddbo.ViewAllSessionInfo
that selects fromsys.dm_exec_sessions
andsys.dm_exec_requests
, and is not filtered to only display the row for the current login or SPID /session_id
.Behavior:
A
(the app login) can executedbo.ViewAllSessionInfo
, but only gets 1 row returned, the row for the current session.EXECUTE AS N'P'
clause (P
has the required permission).A
executesdbo.ViewAllSessionInfo
, but still only gets 1 row returned, the row for the current session (even though the stored procedure is now executing asP
, andP
does haveVIEW SERVER STATE
permission, and the database hasTRUSTWORTHY
enabled).O
is then granted theAUTHENTICATE SERVER
permission.A
executesdbo.ViewAllSessionInfo
, rows for all sessions are returned (even though the only new permission added was granted toO
, and not to eitherA
or even toP
).Scenario #2
occurs when creating or altering an assembly, or even trying to use any code (stored procedure, trigger, functions, or type) from within an assembly that has been marked as either
EXTERNAL_ACCESS
orUNSAFE
. Additionally, starting in SQL Server 2017, if you keep the "security" setting of "CLR strict security" enabled (the default), then even assemblies marked asSAFE
need to meet this requirement (again, we are assuming that neither module signing nor "trusted assemblies" is being used). In this case, assuming that a non-dbo
account is attempting one of those actions, the login associated with thedbo
user of the current database (i.e. same SID between the login anddbo
) needs to have either theEXTERNAL ACCESS ASSEMBLY
orUNSAFE ASSEMBLY
permission. This is more widely known than Scenario #1 (due to an error message that provides this information), yet many people simply resort to havingsa
own the database (again, everything "magically" works). Many people, I'm sure, who already have their DBs owned bysa
might never even know that this is a potential problem because the condition was already satisfied when they enabledTRUSTWORTHY
in order to get the operation working.EXAMPLE:
Setup:
[TestDB]
, that hasTRUSTWORTHY
set toOFF
(different than test case for Scenario #1).[TestDB]
is owned by loginO
(this means that thedbo
user in[TestDB]
has the same SID as loginO
).O
is not a member ofsysadmin
nor hasCONTROL SERVER
permission, or any high-level permission.A
that has an associated userA
in database[TestDB]
. LoginA
is not a member ofsysadmin
nor hasCONTROL SERVER
permission, or any high-level permission.C
, in database[TestDB]
. AssemblyC
has aPERMISSION_SET
ofUNSAFE
.[TestDB]
nameddbo.DoUnsafeStuff
that is really a .NET method in assemblyC
.Behavior:
A
(the app login) can executedbo.DoUnsafeStuff
, but gets a security error.[TestDB]
is altered to setTRUSTWORTHY
toON
.A
can executedbo.DoUnsafeStuff
, but still gets a security error.O
is then granted theUNSAFE ASSEMBLY
permission.A
executesdbo.DoUnsafeStuff
, the stored procedure operates as expected, without the security error (even though the only new permission added was granted toO
, and not toA
).In both scenarios,
the required permission is not required for the principal performing the action. It's more of a gate-keeper to determine if the requested action is allowed to be performed by any principal within that database.