资源调控器安全性

资源调控器使用现有的 SQL Server 安全机制,如身份验证、权限级别以及所有权链。本主题介绍在配置和使用资源调控器时,为确保解决潜在的安全问题而应当考虑的各个方面。

注意事项

需要考虑资源调控器设计和实现的下列元素,以确保此功能在使用时尽可能安全:

  • 权限

  • 资源池和工作负荷组名称

  • 分类器用户定义函数

权限

更改或查看资源调控器设置需要下列权限:

  • 若要更改资源调控器配置,用户需要 CONTROL SERVER 权限。执行任何资源调控器 DDL 语句时,都会检查权限。

  • 若要查看动态管理视图提供的活动配置,用户需要 VIEW SERVER STATE 权限。

建议为经验丰富的数据库管理员授予创建或更改资源调控器配置的权限。

资源池和工作负荷组名称

所有资源池和工作负荷组名称都是公开的。创建池和组时,应确保所选定的名称不会泄露有关服务器上运行的应用程序的特性信息。例如,从工作负荷组 CompanyPayroll 的名称可一眼看出使用该工作负荷组的应用程序的特性和重要性。

分类器用户定义函数

分类器用户定义函数 (UDF) 存储在 master 数据库中。

此函数在设计和实现上与 LOGON 触发器类似,并作为登录过程的一部分在 LOGON 触发器之后运行。此函数在发出请求的会话的登录上下文中执行,并且分类必须在实际建立会话之前完成。因此,来自分类器函数内部且通常将到达用户的所有消息(例如错误消息和来自 PRINT 语句的消息)会转移到 SQL Server 错误日志。

注意事项注意

尽管本版本的资源调控器实现架构绑定以限制可以从分类器 UDF 中发出的调用,但从该函数返回的所有数据并不一定是安全的。有关详细信息,请参阅编写分类器函数的注意事项

请注意分类器用户定义函数行为的以下方面:

  • 默认情况下,资源调控器在登录用户的上下文中作为分类的一部分运行此函数,如果在此函数中指定了 EXECUTE AS,则以指定的用户身份运行此函数。

  • 当资源调控器作为分类的一部分运行此函数时,不会检查分类器 UDF 的 EXECUTE 权限。但是,此函数引用的所有对象都会受到标准的权限检查,这可能允许基于所有权链来进行访问。

  • 如果某个函数在资源调控器分类的作用域之外使用,则将该函数注册为资源调控器分类器将不会影响它的权限级别。

资源调控器中的所有权链

可以依赖默认的基于架构的所有权链接或者使用 EXECUTE AS 来允许单个用户在资源调控器分类运行时访问某个架构。下面的代码示例和注释说明了所有权链接在资源调控器中的工作方式。

注意注意

下面的示例假定启用了 SQL 登录名。

下面的代码创建对 master 具有访问权限的架构用户(SchemaUser1 和 SchemaUser2)。

use master
go

CREATE LOGIN SchemaUser1 WITH PASSWORD='your password here';
CREATE USER SchemaUser1 FOR LOGIN [SchemaUser1];
CREATE LOGIN SchemaUser2 WITH PASSWORD='your password here';
CREATE USER SchemaUser2 FOR LOGIN [SchemaUser2];
go

下面的代码创建一个具有默认登录权限的用户 (NormalUser1)。

CREATE LOGIN NormalUser1 WITH PASSWORD='your password here';
CREATE USER NormalUser1 FOR LOGIN [NormalUser1];
go

下面的代码创建架构(Schema1 和 Schema2)并将它们映射到创建的架构用户。它还为这些架构创建一个表 (groupTable)。

CREATE SCHEMA Schema1 AUTHORIZATION SchemaUser1
CREATE TABLE groupTable (uname sysname, gname sysname);
CREATE SCHEMA Schema2 AUTHORIZATION SchemaUser2
CREATE TABLE groupTable (uname sysname, gname sysname);
go

下面的代码向 groupTable 中添加值。

INSERT Schema1.groupTable VALUES(N'NormalUser1',N'Group1');
INSERT Schema2.groupTable VALUES(N'NormalUser1',N'Group2');
go

此时,Schema1 和 Schema2 分别归 SchemaUser1 和 SchemaUser2 所有。下面的代码示例创建一个将用来访问 Schema1 和 Schema2 的函数。

CREATE FUNCTION Schema1.classifier() RETURNS sysname WITH SCHEMABINDING AS
BEGIN
      DECLARE @n sysname
      SELECT @n = gname FROM Schema1.groupTable WHERE uname = SUSER_NAME()
      SELECT @n = gname FROM Schema2.groupTable WHERE uname = SUSER_NAME()
      RETURN @n
END
go

下面的代码将前面的函数注册为分类器 UDF。请注意 SchemaUser1 没有访问 Schema2 的权限。

ALTER RESOURCE GOVERNOR WITH (CLASSIFIER_FUNCTION=Schema1.classifier);
ALTER RESOURCE GOVERNOR RECONFIGURE
go

为进行测试,尝试从另一客户机连接以 NormalUser1 身份登录。打开 Windows 事件查看器。您应该会在应用程序日志中看到一个分类器出错的条目。NormalUser1 通过所有权链接从 Schema1.classifier 继承对 Schema1.groupTable 的访问权限。但是,Schema1 没有访问 Schema2.groupTable 的权限。

为进行另一测试,为 SchemaUser1 授予对 Schema2.groupTable 的 SELECT 权限。 

GRANT SELECT ON Schema2.groupTable TO SchemaUser1
go

以 NormalUser1 身份登录。您会再次在事件日志中看到一个分类器出错的条目。出现此错误是由于服务器进行检查以确定 NormalUser1 是否有 SELECT 权限,而该权限不会从 SchemaUser1 继承。

在下一个代码示例中,创建了另一个分类器函数。这一次为登录授予 EXECUTE AS SchemaUser1 权限。

CREATE FUNCTION Schema1.classifier2() RETURNS sysname WITH SCHEMABINDING, EXECUTE AS 'SchemaUser1' AS
BEGIN
      DECLARE @n sysname
      SELECT @n = gname FROM Schema1.groupTable WHERE uname = SUSER_NAME()
      SELECT @n = gname FROM Schema2.groupTable WHERE uname = SUSER_NAME()
      RETURN @n
END
go

ALTER RESOURCE GOVERNOR WITH (CLASSIFIER_FUNCTION=Schema1.classifier2);
ALTER RESOURCE GOVERNOR RECONFIGURE;
go

因为新函数运行于 SchemaUser1 的上下文中,并且 SchemaUser1 对 Schema2.groupTable 具有 SELECT 权限,所以对于 NormalUser1 登录名,Schema1.classifier2() 函数将会成功运行。

再次以 NormalUser1 身份登录并查看事件日志中是否有分类器出错条目。

注意注意

因为没有为 NormalUser1 授予对 Schema1.classifier2 函数的 EXECUTE 权限,所以 NormalUser1 无法将此函数作为即席查询运行。

有关详细信息,请参阅所有权链

测试分类器函数

在使用分类器函数对传入请求进行分类之前,应该先对它进行测试和优化。性能低劣的函数会由于超时并公开配置信息而使系统不可用。启用了资源调控器的情况下可以使用专用管理员连接 (DAC) 来解决分类器函数的问题,因为此连接不进行分类。建议在服务器上启用 DAC。有关详细信息,请参阅使用专用管理员连接

注意注意

如果没有 DAC 可用来进行故障排除,则另一个选择是在单用户模式下重新启动系统。单用户模式不进行分类;但是它使您无法在资源调控器分类器运行时对它进行诊断。