所有权链是SQL Server安全特性或安全风险

​译自 K. Brian Kelley 的博文

我听说过,SQL Server内部存在某种被称为“所有权链”的内容,但我并不知道那是什么,或者其是怎么工作的。我想知道其是一个安全风险还是一个安全特性。如果是安全特性,我怎么使用它;如果是一个风险点,我想知道如何对其进行防御。

所有权链是SQL Server内部的一个安全特性,而不是安全风险点。所有对象,如表和视图,都有所有者。在SQL Server 2005 及以上,拥有者可能间接来自对象所述的架构的拥有者。让我们更详细的看看这一点。

每个对象都包含在一个架构中。架构是SQL Server 2005 及以上版本允许对对象进行逻辑分组的容器。例如,对于一个特别的应用,在公司内有两种不同类型的用户,一些来自市场部,一些来自人力资源。某些对象,如有关人事的信息表,逻辑上适合在人力资源类对象的容器中。其他对象适合在市场部相关对象的容器中,如一个跟踪广告活动的表格。通过使用架构,我们可以根据某个目的将对象分组。例如,我们可以创建两个架构:

CREATE SCHEMA Marketing
GO
CREATE SCHEMA HumanResources
GO

架构需要有拥有者。如果创建架构的时候没有指定拥有者(使用AUTHORIZATION关键词设定拥有者),那么创建架构的用户将成为拥有者。我们可以使用下面的语句查看架构及架构的拥有者:

SELECT 
      name AS SchemaName
      , schema_id
      , USER_NAME(principal_id) AS [Owner]
FROM    sys.schemas

下面是从测试库中获得的结果:

在SQL Server 2005 及以上版本中,当一个对象被创建时,并没有必要一定要指定拥有者。SQL Server要做的是,假设对象的拥有者是其所属架构的拥有者。例如,在下面的创建语句中,没有指定拥有者。因此,那些对象的拥有者对应其所属架构的拥有者:

CREATE TABLE HumanResources.TestTable (
      TableValue INT );
GO
CREATE PROC Marketing.QueryTestTable
AS
BEGIN
    SELECT  TableValue
    FROM    HumanResources.TestTable;
END
GO

第一个对象,一个被成为Test Table 的表,在HumanResources架构下被创建。第二个对象,一个被命名为QueryTestTable的存储过程,在Marketing 架构下被创建。因为没有其他T-SQL语句被执行来指定每个对象对应的拥有者,在考虑到所有权链时,SQL Server 将使用各自对象所属架构的所有者作为对象的所有者。可以通过ALTER AUTHORIZATION 显式改变一个对象的所有者,如下(CREATE USER语句创建一个有效用户,以便创建表的所有者):

CREATE USER TestUser WITHOUT LOGIN
GO
CREATE TABLE Marketing.OwnedTable(
      TabeleValue INT
);
GO
ALTER AUTHORIZATION ON Marketing.OwnedTable TO TestUser

我们可以通过查询 sys.objects 和 sys.schemas,查看对象,及其所属架构,并且谁是有效对象拥有者。如果没有显式指定对象拥有者,那么对象中的principal_id列将是NULL。通过使用COALESE,我们可以先查看sys.objects的principal_id,然后再查看sys.schemas。

SELECT
      so.[name] AS ObjectName
      ,sch.[name] AS SchemaName
      ,USER_NAME(COALESCE(so.principal_id,sch.principal_id)) AS [Owner]
FROM sys.objects so
JOIN sys.schemas sch
ON so.[schema_id]=sch.[schema_id]
WHERE [type] IN ('U','P');

下面是test数据库查询的结果:

一旦我们理解了对象的有效所有者,我们可以查看所有权链。当下面事实发生时,产生所有权链:

  • 一个对象引用另外一个对象,如存储过程引用表

  • 两个对象有相同的所有者

这种情况下,仅仅需要第一个对象的权限。以前面 Marketing.QueryTestTable存储过程为例,其查询HumanaResources.TestTable。如果这两个对象的所有者相同,那么就形成了所有权链。当所有权链形成时,SQL Server 将仅仅检查第一个对象的权限。假设,因为所有者相同,这个引用是刻意设计的,将不会检查引用对象的权限。因此,在给定的例子中,如果一个用户有执行Marketing.QueryTestTable的权限,SQL Server将允许执行HumanResources.TestTable,只要查询来自存储过程。

例如,如果我们想控制表的访问,使得用户必须通过存储过程才能访问表,这个T-SQL创建了一个角色,并且赋予了需要的权限给角色:

CREATE ROLE GrantPermissions;
GO
GRANT EXECUTE ON Marketing.QueryTestTable TO GrantPermissions;
GO

通过所有权链的使用,我们可以强制从存储过程中访问。这是很有帮助的,例如,如果我们想控制一个表中数据的插入、更新或者删除。一个很好的例子是应用每次删除一行数据。假设用户显示赋予访问表的权限,那么用户可能意外的删除表中所有的数据,因为忘记了WHERE条件。解决方案是创建一个存储过程,基于传入参数,删除合适的记录。存储过程仅仅允许删除一行。因此,一个终端用户不会意外删除表中所有记录,因为用户没有操作表的权限。如果用户尝试这样操作,他或她将得到一个拒绝访问的错误。然而,用户可以执行存储过程,做什么由存储过程严格控制。这是所有权链提供的好处。

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