理解SQL Server中的跨数据库所有权链

译自K. Brian Kelley 的博文

 

我已经掌握了所有权链,当我想知道跨数据库的所有权链是怎样的?其是如何工作的?如果拥有者是基于数据库的用户的,那么跨数据库的拥有者如何确定?

跨数据库所有权链,是所有权链的一个扩展,除非其确实跨越了数据库的边界。如果你不熟悉所有权链,你应该从这篇《所有权链在SQL Server安全特性或安全风险》早期文章开始。跨数据库所有权链可能发生的一个实例是,如果你在一个数据库中有一个视图引用了另外一个数据库的一个表。视图在第一个数据库中,引用了第二个数据库中的表。如果我们谈论的是在相同数据库中的对象,如果表和视图的拥有者都是相同的用户,将会形成所有权链,终端用户将仅需要视图的访问权限。在跨数据库所有权链中,除跨数据库不同外,其他可能都一样。

跨数据库所有权链既可以在实例级别开启,也可以在数据级别开启。如果跨数据库所有权链在实例级开启,那么对于实例上的所有数据库将都开启跨数据库所有权链,不管个别数据库的设置是怎样的。默认情况下,实例级跨数据库所有权链是关闭的,除下面三个数据库外,其他数据库也是关闭的:

  • master

  • msdb

  • tempdb

这三个系统数据库需要跨数据库所有权链处于开启状态。这三个之外,一般规则,因为安全的影响,应该是:

  • 实例级跨数据库所有权链应该开启

  • 仅仅当需要时,才可以开启数据级跨数据库所有权链。

你可以通过如下语句(SQL Server 2005及以上版本),确定是否开启实例级跨数据库所有权链。如果value值是0,实例级是关闭的;1则表示开启状态。

SELECT 
      name, value
FROM    sys.configurations
WHERE   name = 'cross db ownership chaining'

这也适用于sys.databases 中的is_db_chaining_on 列。我们可以通过查询sys.databases 来查看哪些数据库开启了跨数据库所有权链:

SELECT
      name AS DBName
      ,is_db_chaining_on
FROM sys.databases
ORDER BY name

对于开启跨数据库所有权链的数据库,允许在数据层形成所有权链。所有权链的确定方式和数据库中的所有权链相似。不同的是,如果可能的话,每个对象的拥有者最终都映射为同一个对象(如果必须形成跨数据库所有权链)。

在SQL Server 2005及以上版本中,可能创建一个没有登录账户的用户。为确定这些映射,下面的查询将展示存储过程和用户表的最终拥有者:

SELECT
      so.name AS ObjectName
      ,sch.name AS SchemaName
      ,sp.name AS LoginName
      ,USER_NAME(COALESCE(so.principal_id,sch.principal_id)) AS OwnerUserName
      ,so.type_desc AS ObjectType
FROM sys.objects so
JOIN sys.schemas sch
ON so.[schema_id]=sch.[schema_id]
JOIN sys.database_principals dp
ON dp.principal_id=COALESCE(so.principal_id,sch.principal_id)
LEFT JOIN master.sys.server_principals sp
ON dp.sid=sp.sid
WHERE so.[type] IN ('U','P');

因此,如果在一个数据库中有对象访问第二个数据库中的一个对象,两个数据库都配置启用了数据库所有权链(或者在实例级配置),并且两个对象拥有者相同,那么跨数据库所有权链将形成。和普通的所有权链相同,将在检查第一个对象的权限,而不是第二个。然而,有一个问题使它不同于正常的所有权链。查询一个对象的登陆账户,必须能访问第二个数据库。在master、msdb或tempdb中,将由guest用户完成。但是如果登陆账户没有连接第二个数据库的权限,查询将会失败。

下面展示一个不同配置表(假设两个数据库都开启跨数据库所有权链):

Access to 1st DB Access to 2nd DB Guest User Enabled on 2nd DB? Cross Database Ownership Forms?
Yes No No No
Yes No Yes Yes
Yes Yes No Yes
Yes Yes Yes Yes

如果跨数据库所有权链不能形成,那么,如果一个对象引用和该对象所在数据库不同的另一个数据库的对象,登陆账户必需映射到每个数据库,并且拥有对象合适的权限。

下面是一个例子帮助说明这个问题

USE MASTER;
GO
-- 创建测试数据
CREATE DATABASE Original_Database;
GO
CREATE DATABASE Chained_Database;
GO
USE Chained_Database;
GO
--修改数据库所有者
EXEC sp_changedbowner 'sa';
GO
CREATE TABLE dbo.ATable (TableID INT);
GO
USE Original_Database;
GO
EXEC sp_changedbowner 'sa';
GO
--创建数据库角色
EXEC sp_addrole 'All_Users';
GO
CREATE PROC dbo.QueryATable
AS
BEGIN
  SELECT TableID FROM Chained_Database.dbo.ATable;
END;
GO
GRANT EXECUTE ON dbo.QueryATable TO All_Users;
GO
--创建登陆名
EXEC sp_addlogin 'Standard_Login', 'SomeStrongP4ssword!';
GO
USE Original_Database;
GO
--创建用户
EXEC sp_grantdbaccess 'Standard_Login';
GO
EXEC sp_addrolemember @membername = 'Standard_Login', @rolename = 'All_Users';
GO
USE Chained_Database;
GO
EXEC sp_grantdbaccess 'Standard_Login';
GO
-- Attempt to use QueryATable without Cross-Database Ownership Chaining
-- For SQL Server 2000, log in as the created login and attempt to execute the stored procedure
USE Original_Database;
GO
EXECUTE AS LOGIN = 'Standard_Login';
GO
EXEC dbo.QueryATable;
GO
REVERT;
GO
-- 开启夸库所有权链
EXEC sp_dboption 'Original_Database', 'db_chaining', 'true';
GO
EXEC sp_dboption 'Chained_Database', 'db_chaining', 'true';
GO
-- Attempt to use QueryATable after Cross-Database Ownership Chaining has been enabled
-- For SQL Server 2000, log in as the created login and attempt to execute the stored procedure
USE Original_Database;
GO
EXECUTE AS LOGIN = 'Standard_Login';
GO
EXEC dbo.QueryATable;
GO
REVERT;
GO
-- Clean-up
USE MASTER;
GO
DROP DATABASE Original_Database;
GO
DROP DATABASE Chained_Database;
GO
EXEC sp_droplogin 'Standard_Login';
GO

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章