SQL Server安全(7/11):使用跨数据库所有权链接(Cross-Database Ownership Chaining)的跨数据库安全
在保密你的服务器和数据,防备当前复杂的攻击,SQL Server有你需要的一切。但在你能有效使用这些安全功能前,你需要理解你面对的威胁和一些基本的安全概念。这篇文章提供了基础,因此你可以对SQL Server里的安全功能充分利用,不用在面对特定威胁,不能保护你数据的功能上浪费时间。
从让人眼花缭乱的客户端使用连接,通过到处分布的网络,尤其是互联网,关系数据库在各种应用程序里广泛使用。这使数据对任何人,在任何地方都可访问。数据库可以保存人类知识的很大部分,包括高度敏感的个人信息和让国际商务工作的关键数据。
对于想要偷取数据或通过篡改数据来伤害数据的拥有者的 人来说,这些功能使数据库成为有吸引力的目标。确保你的数据安全是SQL Server配置和使用它来保存数据的程序的重要部分。这个系列会探寻SQL Server 2012安全的基本,这样的话你可以保护你的数据和服务器资源,按你需要的安全等级来保护数据,免受这些威胁对你数据的影响。大部分信息对SQL Server的早期版本也适用,回到SQL Server 2005也可以,因为那是微软在产品里彻底检查安全的时候。但我也会谈论只在SQL Server 2012和后续版本里才有的功能。
大部分时间,你只要关注只有一个所有者的,在一个独立数据库里的数据和对象访问安全。但有时候你需要接触到外面的数据库从多个数据库范文数据和对象,它引起一些安全问题,增加了数据访问的复杂性。在这篇文章里,你会学习跨数据库所有权链接,这样的话,你可以接触到跨数据库边界的安全。
所有权链接
大部分时间,你都很可能创建引用其它对象的数据库,所有对象都包含在同个数据库里。例子包括,在同个数据库里访问表的存储过程,在同个数据库里把表链接一起的视图等等。但是有时候,你会需要创建越过数据库边界的访问其它对象的对象。对于大部分,对于跨数据库范文的安全规则都是一致的,只要在同个数据库里。访问的用户需要在它们直接访问的对象上有必要的许可,连续的所有权链接允许SQL Server缩短许可检查等等。毕竟,SQL Server不能放松警惕,不管对象放在哪里!在一个数据库里,所有权链接简化安全管理,同样也适用于跨数据库。
你是否在一个数据库里还是跨过数据库边界,所有权链接如何工作的基本都是一样的。所有的数据库对象有个拥有者,所有者控制在它所有的对象上谁有许可。访问其它对象的对象——例如在SELECT语句里连接多个表的存储过程——构成一个所有权链接,它是连续的,只要单个所有者拥有所有涉及的对象。
在最高层对象上拥有许可的用户的结果——一个存储过程或视图,例如——访问其它对象的用户不需要在潜在对象上有许可,只要这是一个连续的所有权链接。SQL Server在这样的对象上,一旦验证在最高层对象拥有许可,就停止检查许可。这个架构给所有者在潜在对象的访问,更多更好的控制,因为用户只需要在它们直接访问的对象上有许可。
提示:
理解所有权链接只适用于对象许可非常重要,例如SELECT,UPDATE和EXECUTE的操作。SQL Server总在数据定义语句上检查许可,因为这些许可只适用于语句而不是对象。
跨数据库所有权链接
跨数据库所有权链接是所有权链接的延伸,所有的对象——包括用户直接访问的对象和潜在引用的对象——都在一个数据库里。跨数据库所有权链接的唯一区别是跨数据库边界。因此你可以在一个数据库里有个在多个数据库里把数据连接一起的视图。或在多个数据库里访问对象的存储过程。在这些情况下,用户直接访问的源对象取决于在另一个数据库里包含的对象。
所有权链接的两种类型直接的唯一重要区别是访问数据库,对象所有者的主体。数据库用户是在单个数据库里完全包含的主体。即使多个数据库每个有同样的名称,它们是独立的,不同的主体,因此不能在连续的所有权链接里参与,除非所有这些用户映射到同个数据库级别的登录。因此它是相关所有者的登录,不是数据库用户。那就是关键概念:在连续所有权链接里对象的公共所有者是服务器级别的主体,而不是数据库级别的主体。
在内部,SQL Server通过它们的安全ID(SID)而不是用户名来标识对象所有者。在单个数据库里,单个用户拥有的所有对象有一个SID ,作为所有者,因为在数据库里只有一个用户有那个名称。但跨数据,SID是在服务器级别上的终极登录。在不同的数据库里不同的用户可以关联不同登录,因此会有不同的SID。这是用户和登录相比,很可能是一个最容易混淆的方面,因此这点你要确定清楚。
连续的跨数据库所有权链接需要对象所有者的全部——源对象和所有引用的对象——用同样的SID映射到同个登录。
探寻跨数据库所有权链接
这篇文章的代码演示了如何使用跨数据库所有权链接,并探寻它的特点。代码通过在服务器级别创建名为SharedLogin的登录开始,如代码7.1所示。接下来代码使用这个作为用于在所有权链接里对象的共享所有者。
1 USE master; 2 GO 3 4 IF SUSER_SID('SharedLogin') IS NOT NULL DROP LOGIN SharedLogin; 5 CREATE LOGIN SharedLogin WITH password = 'Y&2!@37z#F!l1zB'; 6 GO
代码7.1:创建会是对象所有者的SharedLogin登录的代码
在跨数据库所有权链接里我们要涉及到两个数据库,因此接下来的代码会创建它们。包含用户直接访问对象的数据库名为SourceDB,引用对象的数据库会是ChainedDB。ChainedDB包含一个dbo.AlaskaCity表,包含阿拉斯加三个最大城市的人口的一些数据。代码7.2创建ChainedDB和它的AlaskaCity表,在表里插入一些数据,用2010的人口调查数据。
1 IF DB_ID('ChainedDB') IS NOT NULL DROP DATABASE ChainedDB; 2 CREATE DATABASE ChainedDB; 3 GO 4 USE ChainedDB; 5 GO 6 7 -- Create a table for access from another database 8 CREATE TABLE dbo.AlaskaCity 9 ( 10 AlaskaCityID INT NOT NULL IDENTITY(1, 1), 11 CityName NVARCHAR(20) NOT NULL, 12 Population INT NOT NULL 13 CONSTRAINT PK_AlaskaCity PRIMARY KEY (AlaskaCityID) 14 ); 15 GO 16 17 INSERT INTO dbo.AlaskaCity (CityName, Population) 18 VALUES ('Fairbanks', 31535), ('Anchorage', 291826), ('Juneau', 31275); 19 GO
代码7.2:创建ChainedDB数据库和有插入样本数据的AlaskaCity表的代码
接下来的代码创建SourceDB数据库,包含一个用户在ChainedDB数据库里,从AlaskaCity直接访问,获取数据的视图。如代码7.3所示,包含一个SELECT语句来测试一切正常。如果你以sysadmin登录的话,这一切都会正常,因为在SQL Server实例里你有所有对象的完整访问,有用所有创建的对象,目前是dbo,你应该可以看到阿拉斯加的人口数据。以sysadmin运行是很方便,但对生产使用并不安全,通常用户访问代码不会有所有对象的成员资格。
1 IF DB_ID('SourceDB') IS NOT NULL DROP DATABASE SourceDB; 2 CREATE DATABASE SourceDB; 3 GO 4 USE SourceDB; 5 GO 6 7 -- Create a view that accesses ChainedDB.dbo.AlaskaCity 8 CREATE VIEW dbo.AlaskaCitiesView AS 9 SELECT * FROM ChainedDB.dbo.AlaskaCity; 10 GO 11 12 SELECT * FROM dbo.AlaskaCitiesView ORDER BY Population DESC;
代码7.3:创建SourceDB数据库和在ChainedDB数据库里访问AlaskaCity表的视图的代码
现在我们有2个数据库,一个是在另一个数据库里,有引用另一个对象,以sysadmin运行一切正常。因此现在我们创建一个更实际的例子来打破代码。代码7.4在SourceDB数据库里创建一个映射到SharedLogin的SourceUser,并在AlaskaCitiesView视图上授予SELECT许可给用户。然后代码修改执行上下文到SharedLogin,尝试访问视图。SELECT语句会成功么?
1 USE SourceDB; 2 GO 3 4 -- Create a user in the SourceDB who will access the view 5 CREATE USER SourceUser FOR LOGIN SharedLogin; 6 GRANT SELECT ON dbo.AlaskaCitiesView TO SourceUser; 7 GO 8 9 -- Try accessing the view as SourceUser 10 EXECUTE AS LOGIN = 'SharedLogin'; 11 SELECT * FROM dbo.AlaskaCitiesView ORDER BY Population DESC; 12 GO 13 REVERT;
代码7.4:创建SourceUser数据库用户,映射到SharedLogin,并用代码测试访问视图
没有成功,SELECT语句抛出了一个异常。
我们暂时先退一步。我们有完整的跨数据库所有权链接:视图和表享有共同成员资格,它是我的登录(sysadmin),同时在数据库里映射到dbo。到这里都一切正常。但跨数据库成员资格所有权链接没有在数据库实例里启用,对2个新的数据库也没有启用。接下来我们来启用它。
启用跨数据库所有权链接
在SQL Server实例的全新安装里,使用跨数据库成员资格所有权链接的选项默认是关掉的。这是因为启用这个选项,实例的安全护甲里打开了几个小孔;在这篇文章里稍后会解释。当这个选项禁用时,当代码依赖于成员资格链接是,会生成许可拒绝的错误。(一会你就会看到,在让代码正常工作前,这不是你要修正的唯一问题,但是我们第一个要处理的)。
你可以在服务器级别或数据库级别启用跨数据库成员资格链接,在服务器级别使用T-SQL语句或SSMS。
在服务器级别启用
对所有的数据库,在服务器级别启用跨数据库所有权链接选项。如果你启用它,它是针对所有数据库的,你不能在数据库级别限制它。
使用SSMS,在对象浏览器右击服务器实例,从弹出的菜单选择【属性】。选择【安全性】页,在对话框的底部,你会看到【跨数据库所有权链接】选项,如插图7.1所示。点击【确定】,在实例里的所有数据库会启用它。
插图7.1:使用服务器属性对话框启用跨数据库所有权链接
你也可以使用T-SQL代码做同样的修改,如代码7.5所示。和其它服务器实例选项,设置cross db ownership chaining选项为1来启用它,设回0禁止它。记得使用RECONFIGURE语句,这样的话你不需要重启实例让修改生效。一旦你执行了这个代码,你可以在这个数据库实例里的任何数据库里,使用跨数据库所有权链接。
1 USE master; 2 GO 3 4 EXECUTE sp_configure 'cross db ownership chaining', 1; 5 RECONFIGURE;
代码7.5:使用sp_configure系统存储过程启用跨数据库所有权链接
不管你用SSMS还是T-SQL代码来启用跨数据库所有权链接,现在把它关闭。这不是启用它的最佳方式,除非你想在实例里的每个数据库里从源对象里引用对象。不是的话,在实例级别启用它太不安全,这样做不明智。
只在需要的数据库启用跨数据库所有权链接才是明智的。
在数据库级别启用
如果在服务器级别你禁用了跨数据库所有权链接,如果你想使用它的话,你必须在数据库级别启用它。如果对于一个数据库禁用了,数据库一点也不能参与跨数据库所有权链接,不管是源还是链接数据库。为了让链接能够工作,源和链接数据库的设置必须启用。
对于所有的数据库,当你创建或附加它们的时候,默认跨数据库所有权链接是禁用的。但对master,msdb,tempdb系统数据库是启用的,对于model数据库是禁用的。因为SQL Server内部使用跨数据库所有权链接,对于这些系统数据库,你不能启用或停用它。
提示:如果你分离,重新附加已经启用跨数据库所有权链接的数据库,在附加回数据库后,你需要重新启用它。对于在服务器级别启用的无效,因为它自动应用到所有的数据库。
可惜SSMS在数据库属性对象框里有跨数据库所有权链接启用选项,如果在插图7.2选项页所示,它是只读的。为了修改数据库的这个值,你需要使用代码7.6,对SourceDB 和ChainedDB数据库同时启用。这个代码使用ALTER DATABASE语句的SET DB_CHAINING选项来打开链接的开和关。
插图7.2:对于ChainedDB数据库,跨数据库所有权链接选项是只读的
1 ALTER DATABASE ChainedDB SET DB_CHAINING ON; 2 ALTER DATABASE SourceDB SET DB_CHAINING ON; 3 GO
代码7.6:在两个数据库里,ALTER DATABASE代码来启用跨数据库所有权链接。
在两个数据库里同时启用了跨数据库所有权链接,你可以再次尝试用SharedLogin的安全上下文访问视图;代码7.7再次展示这个代码。
1 USE SourceDB; 2 GO 3 4 EXECUTE AS LOGIN = 'SharedLogin'; 5 SELECT * FROM dbo.AlaskaCitiesView ORDER BY Population DESC; 6 GO 7 REVERT;
代码7.7:使用跨数据库所有权链接访问嫩跨数据库边界的对象
但代码还是返回同样的错误。这次的问题是还有另一个要求:访问视图对象的用户必须要访问链接数据库。用户在潜在的对象上不需要有许可。事实上,用户在那个数据库里不需要任何许可。
代码7.8展示了你可以如何解决这个问题。代码在ChainedDB数据库创建了一个映射到SharedLogin的ChainedUser用户。这给SharedLogin在那个数据库里一个小立足点,这是跨数据库所有权链接正常工作需要的。为了验证SharedLogin在AlaskaCity表上没有SELECT许可,代码尝试从AlaskaCity直接读取数据。但那个尝试失败了,因为SharedLogin和ChainedUser在ChainedDB任何对象上没有许可。
1 USE ChainedDB; 2 GO 3 CREATE USER ChainedUser FOR LOGIN SharedLogin; 4 -- Note that we're not granting the user any permissions in the chained database. 5 GO 6 -- Verify that SharedLogin doesn't have direct access to the AlaskaCity table, even in the ChainedDB database context. 7 EXECUTE AS LOGIN = 'SharedLogin'; 8 SELECT * FROM dbo.AlaskaCity; 9 GO 10 REVERT;
代码7.8:创建映射到在ChainedDB里SharedLogin的用户,但没有任何许可,可以通过尝试从AlaskaCity查询数据
现在你可以执行代码7.7,最后从视图里成功获得数据。
你可以验证跨数据库所有权已经启用,即使使用常见对象所有权,用户在可以同时访问两个数据库。你可以使用低吗7.9关闭跨数据库所有权链接——实际上你只要运行其中一个即可,因为只有2个数据库都是启用,跨数据库所有权链接才可以正常工作——然后再次运行代码7.7.这次你会得到刚才一样的错误。
1 ALTER DATABASE ChainedDB SET DB_CHAINING OFF; 2 ALTER DATABASE SourceDB SET DB_CHAINING OFF; 3 GO
代码7.9:对2个数据库关闭跨数据库所有权链接
这是跨数据库所有权链接要克服的错误。
共同所有权
使用跨数据库所有权链接启用,代码可以正常访问视图的原因是视图和表有共同所有权——SharedLogin登录。代码假定你以sysadmin登录到SSMS,因此在2个数据库里所有创建的对象属于dbo用户,两个都映射到sysadmin用户。(跨数据库所有权链接正常工作并不需要这个,这是让代码简单和专注的一种方法)。你可以使用代码7.10来验证映射,它列出在数据库里用户数据库和视图的架构名称,所有者用户名和所有者登录名。如果你在SourceDB和ChainedDB里运行代码,你会发现OwnerLoginName是一样的,如插图7.3和插图7.4所示,dbo用户映射到sa登录。
1 SELECT 2 so.[name] AS Object, 3 sc.[name] AS [Schema], 4 USER_NAME(COALESCE(so.principal_id, sc.principal_id)) AS OwnerUserName, 5 sp.name AS OwnerLoginName, 6 so.type_desc AS ObjectType 7 FROM sys.objects so 8 JOIN sys.schemas sc ON so.schema_id = sc.schema_id 9 JOIN sys.database_principals dp ON dp.principal_id = COALESCE(so.principal_id, sc.principal_id) 10 LEFT JOIN master.sys.server_principals sp ON dp.sid = sp.sid 11 WHERE so.[type] IN ('U', 'V');
代码7.10:对于用户数据库和视图,列出架构名,所有者用户名和拥有者登录名
插图7.3:在SourceDB数据库里运行代码7.10的结果
插图7.4:在ChainedDB数据库里运行代码7.10的结果
dbo用户在一个数据库里不是必须的,在另一个数据库dbo用户也不是必须的,理解这个非常重要。它们可以映射到完全不同的登录或不映射。在链里,对于所有涉及到的对象,记录的所有者有同个SID才是重要的。你可以使用代码7.11来验证对象拥有者的SID和登录的SID是否一样。(如果你想自己运行代码的话,你会需要修改在最后部分的计算机名称和用户名)。
1 SELECT name, sid FROM SourceDB.sys.database_principals WHERE name = 'dbo' 2 UNION ALL 3 SELECT name, sid FROM ChainedDB.sys.database_principals WHERE name = 'dbo' 4 UNION ALL 5 SELECT 'PC201602202041\sa', SUSER_SID('PC201602202041\sa');
代码7.11:验证dbo用户的SID是否和映射登录的SID一样
在的电脑上,代码7.11的结果数据如插图7.5所示。共同所有权!
插图7.5:在SourceDB数据库和ChainedDB数据库里的dbo用户SID,和在SSMS执行代码的SID。
跨数据库所有权链接风险
启用跨数据库所有权链接会有风险,因为有特殊地位用户的权限滥用。微软描述了2个方式的风险,两个都涉及了跨数据库安全边界的潜在风险。
- 在单个数据库的上下文里,数据库拥有者,db_ddladmin和db_owners数据库角色的成员可以创建其它用户拥有的对象。这些新对象可以在拥有同样所有者的其它数据库里使用对象,无意给了在其它数据库的对象访问。本质上来说,你应该在所有数据库里信任这些特权用户数据,取决于你在数据库实例如何配置安全。
- 有CREATE DATABASE许可的数据库用户可以创建新的数据库或附加数据库到数据库实例。跨数据库所有权链接启用后,这些用户可以从这些新建或附加的数据库访问在其它数据库里的对象。
如果你想启用跨数据库所有权链接的话,这里的关键是你要信任你特权的用户,这些许可已经超过了在特定表里简单访问和数据维护。这也是微软为什么强烈推荐的一个原因,如果你需要启用跨数据库所有权链接,为了控制安全风险,你只为需要的数据库启用。
小结
跨数据库所有权链接还是你保持你数据安全的另一个方法。这些选项禁用的话,对于恶意用户很难访问其它数据库的数据。当在正确的场景和安全的环境里,你可以启用这个选项让对象所有者保持它们数据的完全控制。你应该几乎从不子啊服务器级别启用跨数据库所有权链接。应该是,在只有真正需要的数据库上启用它,并确定保护好特权用户的滥用。
原文链接:
http://www.sqlservercentral.com/articles/Stairway+Series/123545/
注:此文章为WoodyTu学习MS SQL技术,收集整理相关文档撰写,欢迎转载,请在文章页面明显位置给出此文链接!
若您觉得这篇文章还不错请点击下右下角的推荐,有了您的支持才能激发作者更大的写作热情,非常感谢!