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

How to grant db_owner permissions to an application role?

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

精简版

命令:

GRANT CONTROL ON [DatabaseName] TO [ApplicationRoleName];

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

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

研究工作

我正在研究使用 SQL 服务器 应用程序角色

一旦连接到服务器,您的应用程序将运行一个存储过程到 "login" 作为应用程序本身:

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

db_owner

的权限

通常,应用程序以 用户 的身份登录,该用户是 db_owner 固定角色的成员。 db_owner 角色拥有权限:

同时:

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

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

角色权限

所以,现在是时候向角色授予权限了。正在关注 this page's suggestions

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

这很有趣,但它不会授予所有权限 - 它只授予 SELECTINSERTUPDATEDELETE。我想授予 一切 - 特别是当我不知道所有特权是什么(或可能是什么)时。

我瞎试:

GRANT ALL ON Users to [Contoso.exe];

并且 "Messages" 选项卡中出现以下内容:

The ALL permission is deprecated and maintained only for compatibility. It DOES NOT imply ALL permissions defined on the entity.

好的,所以授予 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;

好的,这更接近了,但它只适用于一个 table。

我需要适用于所有 table 的内容:

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

虽然,结果是我错过了一些特权:

当然,我可以更新我的脚本:

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

但是,而不是这个猫捉老鼠的猜谜游戏:我想授予 ALL 权限。

几乎全部

在上面的警告中,SQL 服务器指出 ALL 并未全部授予。但他们确实 document what all does grant:

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

控制所有权限

原来他们都被骗了。对于另一个权限被创建。一 permission to rule them all:

  • CONTROL

Confers ownership-like capabilities on the grantee. The grantee effectively has all defined permissions on the securable. A principal that has been granted CONTROL can also grant permissions on the securable. Because the SQL Server security model is hierarchical, CONTROL at a particular scope implicitly includes CONTROL on all the securables under that scope. For example, CONTROL on a database implies all permissions on the database, all permissions on all assemblies in the database, all permissions on all schemas in the database, and all permissions on objects within all schemas within the database.

他们继续列举当您拥有 CONTROL:

时隐含的权限

好多了。不必知道所有权限,我只授予一个权限。而不是知道哪些权限适用于哪些类型的对象,我只授予一个。并且因为这条线:

A principal that has been granted CONTROL can also grant permissions on the securable.

我不需要 GRANT WITH GRANT:

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

致所有对象

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

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

For example, CONTROL on a database implies all permissions on the database, all permissions on all assemblies in the database, all permissions on all schemas in the database, and all permissions on objects within all schemas within the database.

重申一下,如果您在 数据库 上授予 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.:

How to grant db_owner permissions to another role?

编辑:警告:不要使用应用程序角色 - 它会破坏一切

当您的客户端登录应用程序角色时,该身份是该连接的持久 属性。并启用连接池(这是 ADO.net、ADO 和 ODBC 中的首选和默认选项)连接保持打开状态 long 时间 - 即使在您关闭连接。

当您的应用程序(即网络服务器)尝试打开一个新连接时,它会从连接池中获取一个。 SqlConnection 所做的第一件事是尝试将连接状态重置回默认值(使用 sp_reset_connection)。

sp_reset_connection 尝试做的其中一件事是撤消您是 app 角色 用户的事实。这是不允许的(因为服务器不知道你以前是谁)。因此,通过使用 应用程序角色 ,当您尝试连接到服务器时会突然出错。

"fix"的唯一方法是禁用连接池。

这是您不想做的事情。

所以解决方案是在生产设置中使用应用程序角色。

"You cannot place an application role into a database role" 似乎是不正确的部分。可以将一个(应用程序)角色添加为另一个角色的成员:

EXEC sp_addrolemember 'db_owner', 'ApplicationRoleName';

(从 2012 年开始,此过程已被弃用,取而代之的是新的 ALTER ROLE .. ADD MEMBER 语法。)

GRANT CONTROL在整个数据库上给一个角色,下面会做:

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

USE 是将角色纳入作用域所必需的;引用数据库时,DATABASE:: 范围限定符始终是必需的。

话虽如此,应用程序角色可能不是授予最广泛权限的好人选。它的密码以明文形式传递,除非注意加密连接本身,监控和审计可能会忽略它,并且当不再需要权限时很容易忘记恢复。最后一部分非常隐蔽,因为未还原的应用程序角色激活将在池连接中持续存在,使连接 "stuck" 处于管理模式。备选方案包括为任意操作打开一个带有新凭据的单独连接,以及使用具有 EXECUTE AS 的存储过程来获得无法有效 GRANTed 的权限。

One of the things that sp_reset_connection tries to do is undo the fact that you are app role user. That is not allowed (because the server doesn't know who you were before).
...
The only way to "fix" it is to disable connection pooling.

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

从理论上讲,使用 EXECUTE AS 切换安全上下文是 sp_reset_connection 可以撤消的事情,因为它只需要在 T-SQL 中执行 REVERT(否需要 cookie 值),并且如果没有要恢复的上下文切换则不会出错(REVERT 只会在活动数据库与执行 EXECUTE AS 时不同时调用时抛出错误)。但是,我没有时间对此进行测试(而且这不是推荐的做法)。

Normally the application logs in as a user who is a member of the db_owner fixed role.

现在这肯定不理想(特别是如果拥有数据库的登录名具有任何管理员权限)。是的,它确实使一些任务变得更容易,因为您实际上禁用了所有安全性,因此您永远不会收到权限错误,但这种方法 高度 不安全,即使您确实添加了额外的步骤设置应用程序角色所需的密码。

首选方法(非常精细且非常安全)是使用 Module Signing. By signing your modules (stored procedures, scalar functions, multi-statement TVFs, and triggers), you can give them (the modules) any permissions necessary to perform the code that the encapsulate, with no fear of anyone taking those permissions and doing something unintended with them. In this approach, you are granting permissions to the code, not to users (i.e. people / application logins). I have more details as to why you want to move away from app roles and EXECUTE AS, and switch to module signing: PLEASE, Please, please Stop Using Impersonation, TRUSTWORTHY, and Cross-DB Ownership Chaining。具体例子请看: