如何将db_owner权限授予应用程序角色?

9
我可以帮您翻译成中文。这段文字是关于如何将“db_owner”固定数据库角色的所有权限授予应用程序角色的方法。请注意保留HTML标签,不要添加解释。以下是需要翻译的内容:

如何将“db_owner”固定数据库角色的所有权限授予应用程序角色?

简短版本

命令:

GRANT CONTROL ON [DatabaseName] TO [ApplicationRoleName];

我想要的是这样的,但它失败了:

Msg 15151,Level 16,State 1,Line 23
无法找到对象“DatabaseName”,因为它不存在或您没有权限。

研究工作

我正在研究使用SQL Server应用程序角色

  • 它类似于用户(具有用户名和密码)
  • 它也像一个角色

一旦连接到服务器,您的应用程序将运行存储过程来"登录"自身作为应用程序:

EXECUTE sp_SetAppRole @rolename = 'Contoso.exe', @password =
'Tm8gaSBkaWRuJ3QganVzdCBiYXNlNjQgZW5jb2RlIGEgcGFzc3dvcmQuIEl0J3Mgb25seSBhbiBleGFtcGxlIQ==';

db_owner 的权限

通常应用程序登录为一个用户,该用户是db_owner 固定角色的成员。 db_owner 角色具有以下权限:

  • 对所有内容的完全访问权限
  • 对每个表的完全访问权限
  • 对每个视图的完全访问权限
  • 对所有现有对象的每个存储过程、函数的完全访问权限
  • 对所有将来存在的对象的完全访问权限

虽然:

  • 您可以将用户放入数据库角色
  • 您可以将用户放入应用程序角色

但您不能将应用程序角色放入数据库角色中。

那么问题来了:如何授予我的应用程序角色所有权限(即执行所有操作的权限)?

角色的权限

现在是授予权限给角色的时候了。根据此页面的建议

GRANT SELECT, INSERT, UPDATE, DELETE ON Users TO [Contoso.exe];

这很有趣,但它并没有授予所有权限 - 它只授予SELECTINSERTUPDATEDELETE。我想授予全部权限 - 特别是当我不知道所有权限时(或可能是什么)。
我盲目尝试:
GRANT ALL ON Users to [Contoso.exe];

以下内容出现在“消息”选项卡中:

ALL 权限已被弃用,仅为兼容性而维护。它并不意味着在实体上定义的所有权限。

好的,所以授予 ALL 并不意味着授予所有权限。这太可怕了。

因此,我回到了:

GRANT SELECT, INSERT, UPDATE, DELETE ON Users TO [Contoso.exe];

除此之外并不能授予所有权限。例如,我碰巧知道有一种能力可以授予他人特权(这是db_owner拥有的一项特权)。因此,我必须修改我的陈述:

GRANT SELECT, INSERT, UPDATE, DELETE ON Users TO [Contoso.exe] WITH GRANT OPTION;

好的,那么这已经更接近了,但它只适用于一个表格。

我需要适用于所有表格的东西:

EXECUTE sp_msForEachTable 'GRANT SELECT, INSERT, UPDATE, DELETE ON ? TO [Contoso.exe] WITH GRANT OPTION;';

尽管如此,我错过了一些权限:
  • SELECT ✔️
  • INSERT ✔️
  • UPDATE ✔️
  • DELETE ✔️
  • REFERENCES ❌
  • ALTER ❌
当然,我可以更新我的脚本:
EXECUTE sp_msForEachTable 'GRANT SELECT, INSERT, UPDATE, DELETE, REFERENCES, ALTER ON ? TO [Contoso.exe] WITH GRANT OPTION;';

但是,与其玩猜谜游戏:我想授予所有权限。

所有几乎就是全部

在上面的警告中,SQL Server指出所有并不能授予全部权限。但是他们确实记录了所有可以授予的权限:

| Permission | Table | View | SP | Scalar UDF | Table UDF |
|------------|-------|------|----|------------|-----------|
| SELECT     |  ✔️   |  ✔️ |    |            |     ✔️    |
| INSERT     |  ✔️   |  ✔️ |    |            |     ✔️    |
| UPDATE     |  ✔️   |  ✔️ |    |            |     ✔️    |
| DELETE     |  ✔️   |  ✔️ |    |            |     ✔️    |
| REFERENCES |  ✔️   |  ✔️ |    |    ✔️      |     ✔️    |
| EXECUTE    |       |      | ✔️ |    ✔️     |            |
| ALTER      |  ❌   |  ❌ | ❌ |    ❌     |      ❌   | 

控制所有权限

事实证明,他们全都被欺骗了。因为另一个权限被创建了。一个支配所有权限的权限

  • 控制(CONTROL)

赋予被授权人类似所有权的能力。被授予 CONTROL 的主体在可保护的对象上实际上拥有所有已定义的权限。被授予 CONTROL 的主体也可以在可保护的对象上授予权限。由于 SQL Server 安全模型是分层的,特定范围内的 CONTROL 隐含包括该范围下所有可保护对象的 CONTROL。例如,在数据库上授予 CONTROL 意味着对数据库、数据库中所有程序集、数据库中所有架构以及数据库中所有架构中的对象拥有所有权限。

他们接着列举了当你拥有CONTROL时隐含的权限:

  • ALTER(更改)
  • CONTROL(控制)
  • DELETE(删除)
  • EXECUTE(执行)
  • INSERT(插入)
  • RECEIVE(接收)
  • REFERENCES(引用)
  • CONTROL(控制)
  • TAKE OWNERSHIP(取得所有权)
  • UPDATE(更新)
  • VIEW CHANGE TRACKING(查看更改跟踪)
  • VIEW DEFINITION(查看定义)

这样就好多了。我只需要授予一个权限,而不是知道所有的权限。而且,我只需要授予一个适用于所有对象的权限。而且,由于下面这行:

被授予 CONTROL 权限的主体也可以在可保护的对象上授予权限。

我就不必再使用 GRANT WITH GRANT 了:

-- when you have CONTROL you also get WITH GRANT for free
EXECUTE sp_msForEachTable 'GRANT CONTROL ON ? TO [Contoso.exe];';

对所有对象

我的问题是我需要给数据库中的每个对象授予 CONTROL 权限。并且每当添加新对象时,我必须确保返回并将其添加到应用程序角色。

我需要的是微软所提示的东西:

例如,在数据库上的 CONTROL 意味着对数据库上的所有权限、数据库中的所有程序集的所有权限、数据库中的所有模式的所有权限以及数据库中所有模式中的对象的所有权限。

换句话说,如果你在 数据库 上授予 CONTROL,那么你将拥有所有权限:

  • 在数据库上
    • 所有对象
    • 数据库中的所有程序集
    • 所有模式

这就是我想要的。我想要将 Grobber 数据库上的 GRANT CONTROL 权限授予 [Contoso.exe] 应用程序角色:

GRANT CONTROL ON Grobber TO [Contoso.exe];

Msg 15151, Level 16, State 1, Line 23
Cannot find the object 'Grobber ', because it does not exist or you do not have permission.

我可能已经接近解决问题,但只停留在了1码线处。
或者,我可能还远远不够。所以我在S.O.上问:
如何将db_owner权限授予另一个角色?
编辑:警告:不要使用应用程序角色-它会破坏一切
当客户端登录应用程序角色时,该身份是该连接的持久属性。并且在连接池打开(作为ADO.net、ADO和ODBC的首选和默认选项)后,该连接会保持打开状态很长时间,即使您关闭了连接。
当您的应用程序(即Web服务器)尝试打开新连接时,它会从连接池中获取一个连接。sp_reset_connectionSqlConnection尝试将连接状态重置为默认状态的第一件事。

sp_reset_connection尝试做的一件事是撤消您作为应用程序角色用户的身份。这是不允许的(因为服务器不知道您之前的身份)。因此,如果使用应用程序角色,则在尝试连接到服务器时会突然出现错误。

唯一的“解决办法”是禁用连接池。

这是您不想做的事情。

因此,在生产环境中不要使用应用程序角色。


1
“好的,所以授予ALL并不是真正的全部授权。这有点可怕。”其实不是,你收到的返回消息告诉你它是一个已弃用的功能;我猜这也是它被弃用的原因之一。 :) - Thom A
@WolfgangKais 一旦连接到数据库,我该如何开始模拟身份?模拟身份是否与连接池兼容(即AppRoles不兼容)? - Ian Boyd
就此而言,在SQL Server 2017中,“ALTER ROLE db_owner ADD MEMBER db_myapprole”按预期工作,同样的,“EXEC sp_addrolemember 'db_owner', 'db_myapprole'”也是如此。我不再拥有SQL Server 2005实例进行测试。2005年的Books Online没有记录它是否适用于应用程序角色 - 它们没有明确列出为支持的,但也没有提到它们不受支持。话虽如此,我不会首先使用应用程序角色来完成这个任务。作为“db_owner”成员的专用帐户更容易,并且对于具有“EXECUTE AS”的偶发性、受限制的使用存储过程也是如此。 - Jeroen Mostert
GRANT CONTROL ON [db] TO [role] 无法生效的原因是一个简单的语法问题:应该使用 GRANT CONTROL ON DATABASE::[db] TO [role]DATABASE:: 范围不可选)。据我所知,这也适用于应用程序角色。我不确定是否更喜欢在数据库上拥有 CONTROL 权限,还是成为 db_owner 的成员;我觉得后者更容易看到。 - Jeroen Mostert
@JeroenMostert,sp_addrolemember确实有效,以及“DATABASE ::”; 谢谢!将您的三个注释作为答案添加,您就可以接受了! - Ian Boyd
显示剩余5条评论
2个回答

7
“你不能将应用程序角色放入数据库角色中”似乎是不正确的部分。一个(应用程序)角色可以作为另一个角色的成员添加:
EXEC sp_addrolemember 'db_owner', 'ApplicationRoleName';

从2012年开始,为了支持新的ALTER ROLE .. ADD MEMBER语法,已经弃用了此过程。

如果想要将整个数据库授予一个角色GRANT CONTROL,则可以执行以下操作:

USE [DatabaseName];
GRANT CONTROL ON DATABASE::[DatabaseName] TO [ApplicationRoleName];

USE命令是必要的,以便将角色引入作用域;当引用数据库时,DATABASE::必须始终使用限定符。

话虽如此,应用程序角色可能不是授予最广泛权限的良好选择。它的密码在传递过程中以明文形式传输,除非采取措施加密连接本身,否则监视和审计可能会忽略它,而且容易忘记在不再需要权限时还原。最后一部分更为隐蔽,因为未还原的应用程序角色激活将持续存在于池化连接中,使连接处于管理员模式下“卡住”。备选方案包括使用新凭据打开单独的连接进行任意操作,以及使用带有EXECUTE AS的存储过程处理不能有效GRANT的权限。


1

sp_reset_connection 尝试的其中一件事情是撤销你作为应用程序角色用户的身份。这是不允许的(因为服务器不知道你之前是谁)。
...
唯一的“解决”方法是禁用连接池。

取消应用程序角色不能由sp_reset_connection自动完成,但它可以手动完成(从SQL Server 2005开始)。只需要通过sp_unsetapprole手动完成。这需要指示sp_setapprole生成一个cookie(VARBINARY值),在执行该系统过程时捕获它,并将其存储在某个地方(在SQL Server中通过CONTEXT_INFO或更新的SESSION_CONTEXT,或在应用程序层中)。然后,在关闭SqlConnection之前(即将该连接返回到池中)将该值传递回sp_unsetapprole

理论上,使用EXECUTE AS切换安全上下文是可以通过执行T-SQL中的REVERT(不需要cookie值)来撤消sp_reset_connection的,如果没有要还原的上下文切换,REVERT不会出错(只有在调用时活动数据库与执行EXECUTE AS时不同时才会抛出错误)。然而,我没有时间测试这个方法(而且这也不是一种推荐的做法)。

通常应用程序以一个属于db_owner固定角色的用户身份登录。

现在这显然不是理想的情况(特别是如果拥有数据库的登录具有任何管理权限)。是的,这确实使某些任务变得更容易,因为您有效地禁用了所有安全性,以便您永远不会遇到权限错误,但是这种方法非常不安全,即使您添加了设置应用程序角色所需的密码这一额外步骤。

首选方法(非常精细和安全)是使用模块签名。通过对您的模块(存储过程、标量函数、多语句TVF和触发器)进行签名,您可以为它们(模块)授予执行所需代码的任何权限,而不必担心任何人会利用这些权限执行意外操作。在这种方法中,您授予的权限是给代码,而不是用户(即人员/应用登录)。关于为什么要放弃应用程序角色和EXECUTE AS,并切换到模块签名,请参阅 请停止使用模拟、可信任和跨数据库所有权链接。有关具体示例,请参见:

如果代码中存在错误,例如try没有finally,应用程序可能不会调用sp_unsetapprole,而是直接处理连接。然后将连接返回到连接池中,其中包含一个应用角色的毒药。这种方法注定会导致错误。 - Ian Boyd
@IanBoyd 很好的观点。谢谢提及。但是,就记录而言,我肯定并不主张人们使用App角色。我只是指出它们可以被还原。存储过程、UDF、触发器等的EXECUTE AS子句确实更好,但除非你赶时间需要快速解决问题,否则仍然不理想。理想的安全机制仍然是模块签名 - Solomon Rutzky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接