简体   繁体   English

SQL Server:存储过程的EXECUTE AS子句未授予sysadmin权限

[英]SQL Server: EXECUTE AS clause of stored procedure not granting sysadmin permissions

I developped a stored procedure in order to restore a database from a backup file and add an application user to it. 我开发了一个存储过程,以便从备份文件还原数据库并向其中添加一个应用程序用户。 This stored procedure belongs to the master database. 此存储过程属于master数据库。

The issue is that my IT department does not allow me to use an admin user, only to use an EXECUTE AS statement with a sysadmin user. 问题是我的IT部门不允许我使用admin用户,而只能对sysadmin用户使用EXECUTE AS语句。

I can restore the database but I can't find a way to add the user at the end of the process. 我可以还原数据库,但是找不到在过程结束时添加用户的方法。

The code I use: 我使用的代码:

CREATE PROCEDURE [dbo].[myProc] 
@database VARCHAR(50)
WITH EXECUTE AS 'aSysAdminUser' 
AS
BEGIN
    --Restore the database (working)
    --Add the application user    (not working)                  
    SET @sqlScript = 'USE '+@database +';
    CREATE USER [myApplicationUser] FROM LOGIN [myApplicationUser];
    EXEC sp_addrolemember ''db_owner'', ''myApplicationUser'''
    EXEC(@sqlScript)
END

When I run it, I have the following error message: 运行它时,出现以下错误消息:

The server principal "aSysAdminUser" is not able to access the database "database" under the current security context. 服务器主体“ aSysAdminUser”在当前安全上下文下无法访问数据库“数据库”。

Any idea how I could create a user in a dynamic parameter db name from a stored procedure on master db using a EXECUTE AS statement? 知道如何使用EXECUTE AS语句从主数据库上的存储过程中以动态参数数据库名称创建用户吗?

Thanks 谢谢

The issue you are facing is a limitation of Impersonation (ie switching the security context via EXECUTE AS ). 您面临的问题是模拟的限制(即,通过EXECUTE AS切换安全上下文)。 For database-scoped objects, such as stored procedures you are specifying a database-level user in the EXECUTE AS clause, not a server-level login . 对于数据库范围的对象(例如存储过程),您要在EXECUTE AS子句中指定数据库级别的user ,而不是服务器级别的login Hence, this procedure is not really acting as a sysadmin . 因此,此过程实际上并不充当sysadmin But, there is a way to safely grant just this one stored procedure true sysadmin permissions that will allow it to do the steps in your dynamic SQL, namely: 但是,有一种方法可以安全地仅向该存储过程授予真正的sysadmin权限,这将使其能够执行动态SQL中的步骤,即:

  1. CONNECT to the database 连接到数据库
  2. CREATE USER 创建用户
  3. ALTER ROLE 改变角色

The way to do this is through signing the stored procedure with a certificate. 这样做的方法是通过使用证书对存储过程进行签名。 The certificate will then also be used to create a server-level login that will be added to the sysadmin server role. 然后,该证书还将用于创建服务器级登录名,该登录名将添加到sysadmin服务器角色中。 Then, when any user / login (that has EXECUTE permission on this stored procedure) executes this procedure, it will pick up the permissions of the certificate-based login merely by being signed by the same certificate. 然后,当任何用户/登录名(对此存储过程具有EXECUTE权限)执行此过程时,它将仅通过由同一证书签名即可获取基于证书的登录名的权限。

Step 1: Set up the certificate in the [master] database: 步骤1:[master]数据库中设置证书:

USE [master];
GO

CREATE CERTIFICATE [BackupRestoreCert]
    ENCRYPTION BY PASSWORD = N'MyPassword'
    WITH SUBJECT = N'Certificate for Managing Backup/Restore Operation Permissions';
GO

Step 2: Create the login and add to the sysadmin role: 步骤2:创建登录名并添加到sysadmin角色:

CREATE LOGIN [BackupRestoreOps] FROM CERTIFICATE [BackupRestoreCert];

ALTER SERVER ROLE [sysadmin] ADD MEMBER [BackupRestoreOps];

Step 3: Sign the stored procedure with that certificate, creating the link to the new login: 步骤3:使用该证书对存储过程进行签名,创建指向新登录名的链接:

ADD SIGNATURE
    TO [dbo].[myProc]
    BY CERTIFICATE [BackupRestoreCert]
    WITH PASSWORD = 'MyPassword';

All of the above is actually done within the context of the [master] database, though it seemed more readable for explanation to have it broken up. 以上所有操作实际上都是在[master]数据库的上下文中完成的,尽管将其分解似乎更易于理解。 But, in practice it would be a single script that the IT folks run once. 但是,实际上,这将是IT人员运行一次的单个脚本。 Of course, if you ever ALTER that stored procedure, they will have to run the ADD SIGNATURE command again (ie Step 3) as it is lost upon any changes to the definition of the procedure. 当然,如果您曾经ALTER该存储过程,则他们将不得不再次运行ADD SIGNATURE命令(即步骤3),因为对过程定义的任何更改都会丢失该命令。

And that is that. 就是这样。 I have tested this with the stored procedure shown in the question and it did, once I added the signature and the sysadmin role, create the user and add it to the db_owner role. 我已经使用问题中显示的存储过程对此进行了测试,添加签名和sysadmin角色后,它确实创建了用户并将其添加到db_owner角色。

Step 4 (maybe): You could probably remove the EXECUTE AS clause from the stored procedure. 步骤4(也许):您可能从存储过程中删除了EXECUTE AS子句。 If there were any permissions that it did pass on that allowed the initial restore operation(s) to work, those should be now assumed via the certificate-based login as it is marked as sysadmin . 如果它确实传递了任何允许初始还原操作正常运行的权限,则现在应该通过基于证书的登录名(假定为sysadmin )来假定这些权限。


DISCLAIMER 免责声明
I just want / need to make clear that it is understood that, given the stored procedure specified in the question, as it is written, will allow for SQL Injection once this permission is granted. 我只想/需要弄清楚,可以理解的是,给定问题中指定的存储过程(按其编写的方式),一旦授予此权限, 允许进行SQL注入。 This, of course, was already a consequence of the original intention of the IT staff's decision to use the EXECUTE AS clause to grant sysadmin permissions to this stored procedure. 当然,这已经是IT员工决定使用EXECUTE AS子句授予该存储过程sysadmin权限的初衷。 I am just pointing it out now that the above steps will actually make their intended behavior a reality. 我现在要指出的是,上述步骤实际上将使他们预期的行为成为现实。 If there are steps above the dynamic SQL that would error if anything other than a database name were passed in, then great, but this still needs to be stated for others who might be looking to duplicate this and just copying / pasting without understanding the full context. 如果在动态SQL之上存在某些步骤,如果传入了数据库名称以外的任何内容,将会出错,那么很好,但是对于其他想要复制此内容而只是复制/粘贴而不了解完整内容的其他人,仍然需要说明这一点上下文。

There are two things that should be done: 应该做两件事:

  1. While a minor point, the datatype of the input parameter should be sysname (alias for NVARCHAR(128) ) as that is how [name] is defined in [sys].[databases] . 作为次要点,输入参数的数据类型应为sysnameNVARCHAR(128)别名),因为这是在[sys].[databases]定义[name]方式。
  2. The main thing to do is verify that the value passed in is an existing database name, something along the lines of: 主要要做的是验证传入的值是现有的数据库名称,类似于:

     IF (DB_NAME(@database) IS NULL) BEGIN RAISERROR(N'Invalid Database Name', 16, 1); RETURN; END; 

EDIT: 编辑:
I have tried finding another server role that would allow for this that is not sysadmin in the hopes of not using such a privileged role. 我已经尝试找到另一个服务器角色,该角色将允许使用sysadmin角色,而不希望使用这种特权角色。 I have tried dbcreator , securityadmin , serveradmin , and setupadmin , but unfortunately none of them worked :(. 我尝试了dbcreatorsecurityadminserveradminsetupadmin ,但不幸的是,它们都没有起作用:(。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM