<上一帖 |
下一帖>
QUOTE:
摘要
本文描述了SQL Server 2005如何被用于支持行级别和单元级别 安全 (RLS/CLS)。这篇白皮书中提供了RLS和CLS 是如何被用于机密数据库安全需求的示例。
QUOTE:
内容列表
执行摘要
设想并设计原则
Fine-Grained 访问控制
安全标签的简要回顾
术语
为实现行级别和单元级别访问控制的安全标签
数据库解决方案概览
行级别安全
定义标签架构
角色
我能看到什么?
改变基本表
定义视图
插入、更新和删除
外部密钥
结合在一起(部分1)
单元级别安全
SQL Server中的加密
密钥访问控制
改变基本表
定义视图
插入/更新时加密单元数据
物理分割
结合在一起(部分2)
性能
行级别泄漏
评估顺序
缓解
Table-valued功能
加密
应用程序错误管理
缓解摘要
摘要
附录 A –INSTEAD OF 触发器实例
附录 B – 对称密钥加密选项
结论
QUOTE:
执行摘要
本文回顾了行级别和单元级别的基于标签的安全需求。本文介绍了基于Microsoft® SQL Server™2005提供的在安全标签和视图上行级别安全的设计。额外的允许单元级别安全的设计也会被介绍。单元级别 安全也同样受到安全标签的控制。
设想并设计原则
本文的描述方法使得使用数据库应用程序的设想结构同每一位最终用户相关联。这既能被用于Windows集成身份验证也能用于SQL Server身份验证。这就排除了为一个单身份而产生的中间连接池的作用,这就是通常用于可测量和管理的好处。然而,系统是需要审核和有其它安全需求的,正好证明了行级别安全标签与审核需求是有关联的,在所有数据库连接时需要用户身份,因此,连接池通常不是一个选项。
这种设计同样能指导以下这些原则:
1. 依靠SQL Server和Microsoft Windows® 安全模型,避免循环验证体系。
2. 保持简单。在一些普通场景下,大量可能的安全标签被结合在一起变的非常大。应避免在仍然允许很多fine-grained 控制的数据时fine-grained 安全组的增长。
3. 这种设计用来衡量很大的上千万行数据库。
通常,目的是对用户帐户限制访问被管理员所管理的数据。这种设计不能利用行级别或 单元级别 安全来影响管理员,特别是 sysadmin 或 db_owner 的成员。拥有权力的用户总是能够访问存储在所有数据库中的数据。
Fine-Grained 访问控制
基于用户权限的访问控制信息是很多计算机软件的基本原则。Microsoft Windows,例如,用访问控制列表 (ACLs)来控制用户访问NTFS文件系统上的文件和文件夹。Microsoft SQL Server 强制访问控制服务器登录、数据库及数据库中的对象(例如表)。在有些情况下,信息访问控制级别会扩展到特定的级别。Windows可以控制访问用户文件,但不能控制访问用户文件的各个部分。SQL Server 2000,像其它的RDBMSs,能够控制访问表,但不能对表提供行级别或 单元级别的安全。
在一些情况下,需要进行更细级别的访问控制。例如,一个病人和诊断的力表,可能被存储在一个单独的文件或表中。任何医生只能被允许视图自己相关病人的信息。在这种情况下,仅仅对文件设置一个ACL或执行对表一个GRANT/DENY SELECT 即可,不会遇到商业需求。
类似的情况在很多环境下都存在,包括金融、法律、政府和军事应用上。消费者秘密的需求是另一个驱动所控制的数据。
这种典型的数据库需求的方法是通过必要的逻辑程序代码来实现的。应用程序的商务逻辑层被过滤了,例如,在客户端-服务器类型的应用程序,客户端可能需要。对应用程序而言,这种方法是很有效的,但数据事实上是不安全的。一个用户通过Microsoft Access 或一个SQL查询工具连接到后台服务器,他拥有了SELECT的权限就能够无限制地访问表中的所有行。
另外一个通常的能够减轻上面问题的方法,是在存储程序中限制所有数据的访问。用户对潜在的表被拒绝了所有的权限,并且在执行了逻辑过滤的存储程序上授予执行权限。这种方法也有自身的缺点。例如,广告公司用户报告数据库是非常困难的。
什么是对用户帐户自动应用逻辑过滤需来呈现真实表(或视图)的需要呢?这种情况下,所有用户都能够访问病人表,但是对每个用户来说,SELECT * FROM Patient只会返回用户能看到的数据。
在介绍基于SQL Server 2005对这个问题的解决方案之前,我们先介绍安全标签,和一个普通的示例来定义对数据的fine-grained访问控制。
QUOTE:
安全标签的简要回顾
安全标签是描述敏感数据项(一个对象)的信息。是一个包括一个或多个种类记号的字符串。用户(主题)有权限来描述相同的记号。每个主题都有自己的标签。主题的标签被用来同另外访问对象的标签进行对比。
例如,以下表的片断有对安全标签的注解行。
表 1
一个系统包括有表2中所示用户帐户的数据。
表 2
每个用户的清除选项(作为安全标签)决定了他们能够访问哪一行。如果Alice对这张表输入SELECT * FROM <tablename>,她能够得到以下结果。
表 3
如果Bob输入SELECT * FROM <tablename>,他能够看到如表4中的不同结果。
表 4
访问控制能够得到比这更为复杂的结果。能够有更多的安全标签访问标准。例如,除了分级之外,一部分数据能够只对特定项目团队的成员可见。假设项目组叫做PROJECT Q,并且考虑以下例子。
表 5
让我们来更改我们用户的权限。
表 6
我们添加了Charlie,并赋予了清除值为TOP SECRET。我们将 Alice的标签加上PROJECT Q 标记。
现在,如果Alice输入 SELECT * FROM <tablename>,她能够看到表7所示结果。
表 7
如果 Charlie 输入 SELECT * FROM <tablename>,他能够看到如下结果。
表 8
尽管Charlie有TOP SECRET的清除值,但是他没有 PROJECT Q 的标记,所以他不能看到第一行。Alice既有SECRET 标记,也有PROJECT Q 标记,所以她能够看到第一行。第二行需要TOP SECRET的清除值,因此只对Charlie是可见的。
这种基本的方法能够被扩展到额外的标记。在一些世界范围的情况中,安全标签能够包括不同种类的一些标记,并且很多标签结合能够变得非常巨大。
术语
这部分介绍正确描述标签的术语以及标签的对比。一个标签是描述对象敏感性或其它主题权限的字符。标签是标记的集合。每个标记描述了一个特殊的权限。标签中的标记来自一个或多个种类。
表 9 显示了之前例子的详细分类。
表 9
如果主题标签支配对象标签,那么这个主题就能够被该对象所访问。给予两个标签, A 和B,如果标签B的每个种类都与标签A的标记相适合,那么标签 A 被认为支配标签B。决定标记是否适合要依靠每个种类的属性。为了我们的目的,每个种类能够被表现为以下属性。
表 10
这有一些例子进行说明。假设我们有一个两个种类的安全标签架构,如表11所示。
表 11
现在让我们看看标签的例子。在每个情况下,问题是:标签A能支配标签B么?
例子 1
比较这些标签,我们必须比较每个种类中的标记。
分级: 在标签A中SECRET标记适合标签B 中SECRET标记。
间隔: 标签A中的Q 间隔不适合标签B中的Q,G间隔,B上所有的间隔必须在A中出现。
因此,标签A不能支配标签B。
QUOTE:
例子 2
分级:标签A中的TOP SECRET标记适合标签B中的CONFIDENTIAL标记。
间隔:标签A中的 Q,G,BN间隔适合标签B中的 Q,G 间隔,所有标签B中的价格在A中均出现。
因此,标签A支配标签B。
例子3
分级:标签A中的SECRET标记适合标签B中的CONFIDENTIAL标记。
间隔:标签B没有间隔,这就意味这没有间隔需求。
因此,标签A支配标签B。
为实现行级别和单元级别访问控制的安全标签
安全标签可以被应用于很多环境中,不论公共还是私有部门。多级安全 (MLS)信息管理区域(一个管理多敏感级别信息的系统)从19世纪70年代开始发展起来。所以,以对数据和用户定义权限为范例的基于安全标签的设计出现了。
数据库解决方案概览
下面会详细讨论对SQL Server 2005数据库添加基于标签和行级别和单元级别 安全的设计方案。这种设计定位于任何标签架构并且强制使用必须使用SQL Server 2005执行。
这种设计包括:
• 添加结构来定义标签种类和标记。
• 允许用户的标签通过基本标记的成员角色来直接进行定义(例如, Top Secret, Confidential; USA, UK, Taskforce Z)。
• 为 write-up, write-down,和其它控制模型提供写入数据。
• 在数据库中,能够进行可选择的加密来提供单元级别 安全,在 SQL Server 2005中使用了内部的,半管理的证书存储。
• 提供固定的指导方针,开发人员或管理人员能够开发自动执行基本输入选择的工具。
• 通过混合多种行级别安全解决方案,提供能够保护行级别安全弱点的策略。
行级别安全
在SQL Server视图中,我们用于强制行级别的机制。视图允许用户预先设定的查询执行。同样,用户能够访问这个视图,但是被拒绝访问下面的表。 这就防止了用户通过视图间接访问到基本表。我们使用特殊方式构造应用到所有使用必要的逻辑、基于表的强制行级别安全的视图。
在一些情况下,视图被用于预先设置的复杂的查询,以使得能够简单地查询报告单一的数据库对象。我们不能做这些。我们的目的是通过同样的定义简化基本表的视图。用户(或应用程序)将查询或更新这些视图,就像是自身的表一样,并将其加入到其它表中。访问基本表将会被拒绝。
建立这些视图需要我们做以下四件事:
1. 建立一些定义了标签种类和标记的表,并对每一个安全标签赋值。这项操作只需要对每个数据库作一次。
2. 为记号值建立角色。在这个角色中的所有成员会被樱桃指派给用户的标签。这项操作只需要对每个数据库作一次。
3. 对基本表做一些更改。
4. 定义视图。
定义标签架构
我们通过建立少量的定义了元数据的表来开始。这些显示在图1的ER 图表中。tblCategory 表标签包含的种类。对每个种类,有一些可能的值作为种类的域。这些被定义在tblMarking 表中。如果是分等级的种类,有关父子关系的记录被定义在 tblMarkingHierarchy 表中。在这些表中的列基于早期安全标签级别的讨论而不被加以说明。

QUOTE:
下面的SQL语句展示了(概括地)我们如何设置一个标签架构示例。
--Categories
INSERT tblCategory (ID, Name, CompareRule, DefaultRole)
VALUES (1, 'Classification', 'ANY', NULL)
INSERT tblCategory (ID, Name, CompareRule, DefaultRole)
VALUES (2, 'Compartment', 'ALL', 'public')
INSERT tblCategory (ID, Name, CompareRule, DefaultRole)
VALUES (3, 'Nationality', 'ANY', 'public')
INSERT tblCategory (ID, Name, CompareRule, DefaultRole)
VALUES (4, 'Need-to-Know', 'ANY', 'public')
GO
--Classification markings
INSERT tblMarking (CategoryID, MarkingRoleName, MarkingString,)
VALUES (1, 'T', 'T')
INSERT tblMarking (CategoryID, MarkingRoleName, MarkingString)
VALUES (1, 'S', 'S')
INSERT tblMarking (CategoryID, MarkingRoleName, MarkingString)
VALUES (1, 'C', 'C')
INSERT tblMarking (CategoryID, MarkingRoleName, MarkingString)
VALUES (1, 'U', 'U')
--Classification hierarchy
INSERT tblMarkingHierarchy (ParentCategoryID, ParentMarkingRoleName, ChildCategoryID, ChildMarkingRoleName) VALUES (1, 'T', 1, 'S')
INSERT tblMarkingHierarchy (ParentCategoryID, ParentMarkingRoleName, ChildCategoryID, ChildMarkingRoleName) VALUES (1, 'S', 1, 'C')
INSERT tblMarkingHierarchy (ParentCategoryID, ParentMarkingRoleName, ChildCategoryID, ChildMarkingRoleName) VALUES (1, 'C', 1, 'U')
--Compartment markings
INSERT tblMarking (CategoryID, RoleName, MarkingString)
VALUES (2, 'Q', 'Q')
INSERT tblMarking (CategoryID, RoleName, MarkingString)
VALUES (2, 'G', 'G')
INSERT tblMarking (CategoryID, RoleName, MarkingString)
SELECT 2, DefaultRole, 'none' FROM tblCategory WHERE ID = 2
Etc.…
接下来让我们关注tblUniqueLabel表。这张表被用于绘制对于唯一的ID的标记与特殊的安全标签实例的结合。由于效率的缘故,这张表根据需要被定制。一部分数据及其安全标签被添加到数据库中,被称作获取ID的存储进程会表现这些唯一的安全标签。如果在tblUniqueLabel 里没有相应的行,就会添加新的行并返回新的ID。我们想要在这张表中正好有我们需要的行,不多也不少。
最后, tblUniqueLabelMarking 表结合了单独的标记值和安全标签。
角色
定义标签架构,需要对SQL Server安全模型进行一些操作。为我们定义的每个种类中的标签,建立相应的数据库角色 。数据库角色必须坚持以下指导方针。
• 为可以使用任何或全部比较规则的不分等级的种类,为每个可能值建立一个角色。为了这些标记,角色的名字必须在tblMarking.MarkingRoleName 值中标出。
• 为分等级的种类,需要做相同的事。另外,角色必须被嵌套到分等级模型当中。对建立的每个角色,加入在表tblMarkingHierarchy 中定义的父角色作为一个成员。这种嵌套,例如,有Secret 清除选项的用户,能够访问该级别及以下级别的数据。
这些角色允许用户能够被赋予正好能够访问数据的安全标签的权限。这就是程序或数据库管理员(DBA)在用户系统中所应有的角色。
在下部分,我们将看到角色成员是如何被识别,以确定他们能够访问那些数据的。
我能看到什么?
在我们关心这些应用程序表之前,我们先建立一个集成了真正行级别安全逻辑的帮助视图。我们称之为vwVisibleLabels。定义起始点视图如下面代码所示。
SELECT ID, Label.ToString()
FROM tblUniqueLabel WITH (NOLOCK)
WHERE ….
WHERE语句定义了我们标签架构中的基于种类属性。最重要的属性是比较规则。为每个有任何对比规则的种类,添加以下语句到WHERE语句中。
ID IN (SELECT ID FROM tblUniqueLabelMarking WITH (NOLOCK)
WHERE CategoryID = <HardCodedCatID> AND IS_MEMBER(MarkingRoleName) = 1)
这条语句给了所有ID唯一的安全标签,包含了当前用户成员身份种类的标记。让我们更详细的看一下,提交查询扫描tblUniqueLabelMarking 表种类中的行。从这些行当中,选择了作为MarkingRoleName数据库角色成员中当前的一个用户。这些检查通过SQL Server内置更能IS_MEMBER 来完成。对这些行中的每一行,在tblUniqueLabel 中相应记录的ID都会被返回到外部查询。
那么比较规则为全部的种类如何呢?添加以下语句到WHERE语句中。添加以下语句到WHERE语句中。
1 = ALL(SELECT IS_MEMBER(MarkingRoleName) FROM tblUniqueLabelMarking (NOLOCK)
WHERE CategoryID = <HardCodedCatID> AND UniqueLabelID = tblUniqueLabel.ID)
这些语句的结果是需要用户拥有全部为了访问的应用标记。提交查询通过IS_MEMBER对每个在tblUniqueLabel 给出的相关标记返回一个值的列表。如果所有返回值都为1,这些语句适合。
比较规则为完全相反的种类,应该在WHERE 语句中使用如下语句:
ID IN
(SELECT KeyMappingID FROM tblUniqueLabelMarking(NOLOCK) Z
WHERE NOT EXISTS (SELECT MarkingRoleName
FROM tblMarking
WHERE IS_MEMBER(MarkingRoleName) = 1
AND CategoryID = 1 AND InternallyGenerated =0
EXCEPT
SELECT MarkingRoleName
FROM tblUniqueLabelMarking
WHERE CategoryID = 1
AND KeyMappingID = Z.KeyMappingID))
所有语句加入,使得到他们中有一个AND操作者。
结合所有我们之前的例子,你就会得到媒体视图的定义,如下所示:
CREATE VIEW vwVisibleLabels
AS
SELECT ID, Label.ToString()
FROM tblUniqueLabel WITH (NOLOCK)
WHERE
ID IN --Classification
(SELECT ID FROM tblUniqueLabelMarking WITH (NOLOCK)
WHERE CategoryID = 1 AND IS_MEMBER(MarkingRoleName) = 1)
AND --Compartments
1 = ALL(SELECT IS_MEMBER(MarkingRoleName) FROM tblUniqueLabelMarking
WHERE CategoryID = 2 AND UniqueLabelID = tblUniqueLabel.ID)
GO
QUOTE:
这个视图被称作vwVisibleLabels,但不能这么认为“这张列表显示了在数据库中所有我(当前用户)能访问的安全标签”。
表 12 总结了依靠每个种类中属性,设置标签架构的设计规则。
表 12
注意处理一些场景时,有一个额外的属性叫做NoMarkingBehavior。它控制着不带种类标记的访问评估。在多数场景中,没有标记意味着种类能够被忽视。在一些场景中,种类中缺省的标记意味着无人能够访问这些数据(除了通过RLS为帐户赋予了明确的权限)。
改变基本表
现在来看一下被用于到添加了行级别安全的基本表的改变。这些改变是次要的(在我们得到单元级别安全标题是会更多) 两列必须被添加到基本表中: RowLabel 和 RLSMappingID。 RowLabel对这行来说是没有赋值的安全标签 。 RLSMappingID 是从相应安全标签tblUniqueLabel 得到的整型ID。严格地说,只有RLSMappingID 是必需的。事实上,有RowLabel 列是违反标准格式的。在严格的安全场景下,在数据进入数据库时,通常需要安全标签同数据保持在一起。因此,在基本表中有RowLabel。 可能为了其它行的元数据要加入很多必要的列。无论特殊应用程序的策略是什么样的,都应该知道在基本表中RLSMappingID 是必需的。
当一行及其安全标签被插入到一张基本表时,响应这张表的RLSMappingID 必需被返回(或生成),并且被放置在新行中的RLSMappingID 列。同样地,如果一行被更新,导致安全标签被改变,那么 RLSMappingID 也会相应地改变。(安全标签的更新是根据每个场景的安全需要进行的;这是能够被禁止的)。
最终,在基本表RLSMappingID和RLSMappingID列上建立了一个外部密钥关联。
由于性能原因,可以在RLSMappingID列上建立一串索引。不要跳过这步,否则会影响性能!
定义视图
我们准备好了上一步操作。接下来将在基本表上建立一个视图,本质上说,在用户和应用程序看来是取代了这张表。这里就是视图的定义。
CREATE VIEW UserTable
AS
SELECT <base table column list which does not include RLSMappingID, or any columns from vwVisibleLabels>
FROM tblBaseTable (READCOMMTTED), vwVisibleLabels
WHERE tblBaseTable.RLSMappingID = vwVisibleLabels.ID
GO
GRANT SELECT ON UserTable TO <app_users>
DENY ALL ON tblBaseTable TO <app_users>
GO
以下有两部分视图的视图的定义:
• 通过在基本表上对可见标签视图加入安全标签识别符,这个标签需要依靠用户在安全组成员身份来对行控制访问基本表的优势就体现出来了。
• READCOMMITTED锁应用到基本表上以防止恶意读取基本表的数据。这就防止了未经处理的用户视图行中敏感数据的内容,但是那些安全标签标识符没有被改变。这种锁定视图不考虑读取转换隔离级别或在查询视图时所用的外在锁。REPEATABLE READ 和 SERIALIZABLE 在这里是同样可以被接受的。注意这种其它用户(更新者)有转换隔离级别除了READ UNCOMMITTED的假设。
我们现在有一个明显强制的基于标签的行级别安全视图,而没有任何程序逻辑,并且一直起效,即使应用程序层迂回访问。
当然,这种视图仅仅对从表中选择数据有好处。如果应用程序必需在表中插入、更新过删除行,这就只有很少的工作要做。
插入、更新和删除
迄今为止来讨论任何从根本表中选择行。很多应用程序需要在表中写入信息,插入、更新或删除基于标签的行级别安全的表中的行,带来了一些问题。哪一行能够被用户更新?是否是任何限制安全标签阻止用户在行中插入新的内容?
要根据不同场景来回答这些问题。一些系统只允许 “read-down, write-down” 的行为,而其它系统只允许 “read-down, write-up”的行为。这些场景都能被支持,而且使用了很多相同的技术。
来支持插入和更新基本表内容,我们需要能够做以下这些事情:
• 允许插入或更新用户可能的视图。
• 需要正确的行标签。
• 需要生成新的标签来映射ID,或解决已存在ID的标签。
• 为一些场景的需要,强制write-down 或write-up逻辑。
• 完成插入/更新基本表。
这是通过定义为插入和更新定义INSTEAD OF 触发器而完成的。这些触发器检查合法的标签,生成或重新找回映射ID的标签,强制写权限检查,并且完成事实的对基本表的插入或更新。
INSTEAD OF 触发器的例子在附录A中显示,这有一些指出这些的代码。
首先,视图以下两行代码。
DECLARE @RowClassification SecurityLabel
SELECT @RowClassification = row_label FROM inserted
可以得到一些包括INSERT语句的行标签,被称之为用户定义的SecurityLabel 。SecurityLabel 集成了弥补特殊标签的值。SecurityLabel 能够同Dominates功能相比较, 一个Microsoft Visual C#® 用户定义的功能。这种方式比较了两种标签来看适合一个可以支配另一个。我们用在触发器中的这种方式来比较当前用户对他们感兴趣的数据安全标签的权限。
在比较之前,我们必须得到一个描述当前用户权限的SecurityLabel 实例。下面的代码做了这件事。
EXECUTE AS CALLER
SELECT @CallerName = CURRENT_USER
REVERT
DECLARE @UserClearance [SecurityLabel]
exec usp_GetUserLabel @CallerName, @UserClearance OUTPUT
usp_GetUserLabel 是一个检查当前用户角色成员的存储进程,并且生成一个标签来描述他们的权限级别。这个标签返回一个SecurityLabel 实例。我们想能够比较的任何用户权限标签。在我们例子的这种情况下,我们执行了write-down-only的需求,如下所示。
IF dbo.Dominates(@UserClearance, @RowClassification) = 0
RAISERROR('user rights not sufficient to write this data', 12, 1)
相似的代码能够被用于强制write-up 需求。(做为选择,如果需要数据标签插入用户的清除级别,我们能够跳过Dominates 检查并且简化从usp_GetUserLabel 返回的标签。
假设用户有适当的写入数据的权限,我们需要为行标签获取映射ID。usp_GetRLSMappingID 存储进程来执行这项工作。这个程序执行了必须重新找回ID或生成新的ID的工作。
用过映射ID,我们能够执行真正的插入基本表的操作。
更改或删除触发器的代码通常使用了相似的逻辑。
[ 本帖最后由 xiaoxinlucky 于 2008-1-21 16:53 编辑 ]