时间:2021-07-01 10:21:17 帮助过:8人阅读
代码7.1 创建SharedLogin登录名
我们需要两个数据库来演示跨数据库所有权链接,因此接下来代码会创建它们。用户直接访问对象的数据库叫做SourceDB,关联对象的数据库叫做ChainedDB。ChainedDB包含一个dbo.AlaskaCity表,里面有Alaska三大城市的人口数据。代码7.2会创建ChainedDB库和AlaskaCity表,然后往表中插入部分数据:
IF DB_ID(‘ChainedDB‘) IS NOT NULL DROP DATABASE ChainedDB; CREATE DATABASE ChainedDB; GO USE ChainedDB; GO -- Create a table for access from another database CREATE TABLE dbo.AlaskaCity ( AlaskaCityID INT NOT NULL IDENTITY(1, 1), CityName NVARCHAR(20) NOT NULL, Population INT NOT NULL CONSTRAINT PK_AlaskaCity PRIMARY KEY (AlaskaCityID) ); GO INSERT INTO dbo.AlaskaCity (CityName, Population) VALUES (‘Fairbanks‘, 31535), (‘Anchorage‘, 291826), (‘Juneau‘, 31275); GO
代码7.2 创建ChainedDB库和AlaskaCity表并插入测试数据
代码7.3创建SourceDB库和一个视图,用户直接访问视图以返回ChainedDB库AlaskaCity表中的数据。代码中包含一个查询语句测试视图返回数据是否正确。如果你是以sysadmin身份登录这个语句应该正常执行。作为管理员运行是很方便,但是在生产使用它不安全,通常一个用户访问的代码不会有所有对象的所有权!
IF DB_ID(‘SourceDB‘) IS NOT NULL DROP DATABASE SourceDB; CREATE DATABASE SourceDB; GO USE SourceDB; GO -- Create a view that accesses ChainedDB.dbo.AlaskaCity CREATE VIEW dbo.AlaskaCitiesView AS SELECT * FROM ChainedDB.dbo.AlaskaCity; GO SELECT * FROM dbo.AlaskaCitiesView ORDER BY Population DESC;
代码7.3 创建SourceDB库和AlaskaCitiesView视图
现在你有两个数据库,其中一个库下的对象会访问另一库下的对象,而且在sysadmin下执行是正常。代码7.4在SourceDB库创建一个SourceUser用户映射到SharedLogin登录名,并且将AlaskaCitiesView视图的查询权限授予给SourceUser用户。然后代码更改执行上下文到SharedLogin并尝试访问AlaskaCitiesView视图。查询会成功吗?
USE SourceDB; GO -- Create a user in the SourceDB who will access the view CREATE USER SourceUser FOR LOGIN SharedLogin; GRANT SELECT ON dbo.AlaskaCitiesView TO SourceUser; GO -- Try accessing the view as SourceUser EXECUTE AS LOGIN = ‘SharedLogin‘; SELECT * FROM dbo.AlaskaCitiesView ORDER BY Population DESC; GO REVERT;
代码7.4 创建一个SourceUser用户映射到SharedLogin登录名
查询抛出一个错误:服务器主体 "SharedLogin" 无法在当前安全上下文下访问数据库 "ChainedDB"。
我们有一个连续的所有权链接:视图和表共享相同的所有者——dbo数据库用户。但是在服务器实例或新数据库上没有启用跨数据库所有权链接,下面我们将启用它。
启用跨数据库所有权链接
新安装的SQL Server实例跨数据库所有权链接选项默认是关闭的。这是因为启用这个选项会在实例的安全盔甲上打开几个漏洞;我将在这篇后面简要地讲解风险。当选项是禁用的,当代码依赖跨数据库所有权链接就会生成权限拒绝错误。(正如你稍后会看到的,在代码执行前这不是唯一需要修正的问题,但它是第一个我们将解决。)
服务器级别启用
在服务器级别为所有数据库启用跨数据库所有权链接。如果你启用它,它会应用到所有数据库上而且你不能在数据库上限制它。
使用SSMS,对象资源管理器下右击实例->属性->安全性,在对话框的底部你会看到跨数据库所有权链接的选项,如图7.1所示,点击确定实例下的所有数据库就会启用。
图7.1 启用跨数据库所有权链接
你也可以用T-SQL语句启用,如代码7.5所示。比如在其他服务器实例将"cross db ownership chaining "值设为1启用,O禁用。确保使用RECONFIGURE语句而不需要重启实例才能让更改生效。一旦你执行了这段代码,你就可以在SQL Server实例的任何数据库中使用跨数据库所有权链接。
USE master; GO EXECUTE sp_configure ‘cross db ownership chaining‘, 1; RECONFIGURE;
代码7.5 使用T-SQL启用跨数据库所有权链接
如果你使用SSMS或T-SQL已启用了跨数据库所有权链接,请将它关闭。这不是启用它的最好方法,除非你真的打算在实例的每个数据库下从源对象访问关联对象。如果不打算这样,在实例上启用太不安全,而且不是好主意。
作业替代,你应该针对需要的数据库启用跨数据库所有权链接。
数据库级别启用
在服务器级别禁用了跨数据库所有权链接,如果你需要使用的话,你必须在数据库级别启用它。如果一个数据库禁用了,它就不能作为源或链接数据库参与跨数据库所有权链接。为了让链接工作,在源和链接数据库都必须设置启用。
默认当你创建或附加用户数据库时跨数据库所有权链接是禁用的。但是master、msdb和tempdb系统数据库是启用的,model数据库是禁用的。因为SQL Server用户内部使用跨数据库所有权链接,我们不能禁用或启用系统数据的选项。
提示:如果你分离然后再附加一个已经启用跨数据库所有权链接的数据库,你必须在数据库附加后重新启用选项。这不适合于服务器级别已启用的,服务器级别会自动应用到所有数据库上。
不幸地是,在SSMS->数据库属性->选项,"跨数据库所有权链接已启用"选项是False,如图7.2所示。为了更改数据库的这个值,你需要使用代码7.6,启用SourceDB和ChainedDB数据库的设置。
图7.2 查看ChainedDB数据库跨数据库所有权链接已启用选项
ALTER DATABASE ChainedDB SET DB_CHAINING ON; ALTER DATABASE SourceDB SET DB_CHAINING ON;
代码7.6 启用跨数据库所有权链接
两个数据库都启用跨数据库所有权链接后,你可以尝试使用SharedLogin安全上下文再次访问视图:
USE SourceDB; GO EXECUTE AS LOGIN = ‘SharedLogin‘; SELECT * FROM dbo.AlaskaCitiesView ORDER BY Population DESC; GO REVERT;
代码7.7 尝试使用跨数据库所有权链接跨过数据库边界访问对象
还是抛出同样的错误:服务器主体 "SharedLogin" 无法在当前安全上下文下访问数据库 "ChainedDB"。这次的问题是要满足一个需求。用户访问视图必须也能够访问ChainedDB数据库,用户不需底层对象的权限。事实上,用户不需要在那个数据库下的任何权限。
代码7.8展示了如何解决这个问题。在ChainedDB库创建一个ChainedUser用户映射到SharedLogin登录名
。这给SharedLogin在ChainedDB库一个立足点,跨数据库链接工作的必要条件。为了验证SharedLogin在AlaskaCity表上没有查询权限,代码尝试直接从AlaskaCity表上读取数据。但是尝试失败,因为SharedLogin和ChainedUser在ChainedDB库没有任何权限。这次的错误是:拒绝了对对象 ‘AlaskaCity‘ (数据库 ‘ChainedDB‘,架构 ‘dbo‘)的 SELECT 权限。
USE ChainedDB; GO CREATE USER ChainedUser FOR LOGIN SharedLogin; -- Note that we‘re not granting the user any permissions in the chained database. GO -- Verify that SharedLogin doesn‘t have direct access to the AlaskaCity table, even in the ChainedDB database context. EXECUTE AS LOGIN = ‘SharedLogin‘; SELECT * FROM dbo.AlaskaCity; GO REVERT;
代码7.8 ChainedDB库创建一个ChainedUser用户映射到SharedLogin登录名
现在你可以返回执行代码7.7,它会成功并返回视图中的数据!
使用代码7.9禁用数据库所有权链接——实际上你只需要执行代码中任何一条语句,因为要让跨数据库所有权链接正常工作两个数据库的都要设置成ON。然后你再运行代码7.7,此时对象所有者相同且用户能访问两个数据库。这次报错:拒绝了对对象 ‘AlaskaCity‘ (数据库 ‘ChainedDB‘,架构 ‘dbo‘)的 SELECT 权限。
ALTER DATABASE ChainedDB SET DB_CHAINING OFF; ALTER DATABASE SourceDB SET DB_CHAINING OFF;
代码7.9 禁用数据库所有权链接
共用所有权
样例代码中的视图在启用跨数据库所有权链接后能正常执行的原因是:视图和表共用所有权。样例代码假定是以sysadmin登录到SSMS编写的,因此代码所创建的对象都被dbo用户所拥有,用户都是映射到sysadmin。你可以用代码7.10验证映射关系,显示架构名称、所有者用户名、所有者登录名。如果你在SourceDB和ChainedDB库下运行,你会发现所有者登录名是相同的,如图7.3和7.4所示,在我的例子中,dbo用户映射到登录名USER-67NP5R8LGK\Administrator.
SELECT so.[name] AS Object, sc.[name] AS [Schema], USER_NAME(COALESCE(so.principal_id, sc.principal_id)) AS OwnerUserName, sp.name AS OwnerLoginName, so.type_desc AS ObjectType FROM sys.objects so JOIN sys.schemas sc ON so.schema_id = sc.schema_id JOIN sys.database_principals dp ON dp.principal_id = COALESCE(so.principal_id, sc.principal_id) LEFT JOIN master.sys.server_principals sp ON dp.sid = sp.sid WHERE so.[type] IN (‘U‘, ‘V‘);
代码7.10 显示对象架构名称、所有者用户名、所有者登录名
图7.3 SourceDB库下运行结果
图7.4 ChainedDB库下运行结果
要注意的是一个数据库的dbo用户不一定和另一个数据库下的dbo用户相同。它们可以被映射到完全不同的登录名或者不映射。重要的是链接中所有对象的拥有者有相同的SID。你可以用代码7.11验证对象所有者和登录名有相同的SID。
SELECT name, sid FROM SourceDB.sys.database_principals WHERE name = ‘dbo‘ UNION ALL SELECT name, sid FROM ChainedDB.sys.database_principals WHERE name = ‘dbo‘ UNION ALL SELECT ‘USER-67NP5R8LGK\Administrator‘, SUSER_SID(‘USER-67NP5R8LGK\Administrator‘);
代码7.11 验证对象所有者和登录名有相同的SID
图7.5 SourceDB和ChainedDB库下dbo用户,及代码会话登录用户的SID
跨数据库所有权链接的风险
启用跨数据库所有权链接存在风险因素的原因是高特权用户可能滥用。微软以2种方式来描述风险,这两者都涉及到数据库的安全边界的潜在风险:
->在一个数据库中,数据库所有者和db_ddladmin和db_owners数据库角色的成员可以创建其他用户所拥有的对象。这些新对象能够使用其他数据库下具有相同所有者的对象,这样无意中就可以访问其他数据库下的对象。
->具有CREATE DATABASE权限的数据库用户可以创建新数据库和附加数据库。启用跨数据库所有权链接,这些用户可以从新创建的或附加的数据库访问其他数据库对象。
这里的关键是,你要相信高特权用户,那些权限超越了简单的访问和在特定表的维护数据,如果你要允许跨数据库所有权链接。这就是为什么微软强烈建议,如果你需要启用跨数据库所有权链接,只在需要的数据库上启用它,为了遏制安全风险。
总结
跨数据库所有权链接是SQL Server帮助保持你的数据安全的另一种方式。禁用这个选项,恶意用户访问其他数据库中的数据就会更难。但在正确的场景和安全环境中,你可以启用此选项让数据库对象所有者对其数据有更严格的控制。你应该很少在服务器级别启用跨数据库所有权链接。相反,只为你真正需要的数据库启用,并确保不被特权用户滥用。
第七篇 SQL Server安全跨数据库所有权链接
标签: