理解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

 

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