sql-server - 无法使用 EXECUTE AS OWNER 在存储过程中创建登录名?

标签 sql-server impersonation sql-server-2014

正在运行SQL Server 2014 Express。以 sa 身份登录,我尝试执行下面的代码。它给了我以下错误:

Msg 15247, Level 16, State 1, Line 25 User does not have permission to perform this action.

为什么?!如果我在程序中选择SYSTEM_USER,它确实是sa(合法所有者)。

USE [MyDatabase]
GO

CREATE PROCEDURE [dbo].[create_login]
    @Login [nvarchar](256),
    @Password [nvarchar](128)
WITH EXECUTE AS OWNER
AS
BEGIN
    SET NOCOUNT ON

    DECLARE @Sql NVARCHAR(4000)

    SET @sql = N'CREATE LOGIN ' + QUOTENAME(@Login) + N' WITH PASSWORD = '
        + QUOTENAME(@Password, N'''') + N',CHECK_EXPIRATION=OFF, CHECK_POLICY=ON;'
    EXEC (@sql)
END
GO

GRANT EXECUTE ON [dbo].[create_login] TO [my_simple_role]
GO

-- Let's go!
EXEC [dbo].[create_login] N'NewUser', N'c0Mpl3xP@55w0rd'
GO

-- Error! :(

如果我在存储过程之外运行语句,它就会起作用。

我正在尝试创建一个存储过程,允许普通用户添加服务器登录名。上面的好像不行。请指教!

最佳答案

登录是服务器级对象,因此需要服务器级权限。 EXECUTE AS <database-user>是数据库范围的安全上下文。

通过存储过程授予普通用户特权操作的一种方法是使用映射到具有必要权限的登录名的证书对模块进行签名。在这种情况下,必要的步骤是:

  1. 在主数据库中创建证书
  2. 为该证书创建登录名
  3. 授予证书登录权限以创建登录
  4. 从 master 导出证书
  5. 将证书导入应用数据库
  6. 使用证书签署存储过程

下面是从 Erland Sommarskog's web site 收集的示例。请注意,每次更改时,您都需要使用证书对过程进行签名。

--create database master key, if necessary
IF NOT EXISTS(SELECT 1 FROM sys.symmetric_keys WHERE name = N'##MS_DatabaseMasterKey##')
BEGIN
    CREATE MASTER KEY ENCRYPTION BY PASSWORD='M@sterkEEPassw0rd';
END;
GO

CREATE CERTIFICATE SecurityAdministratorCertificate
   WITH
     SUBJECT = 'Allows non-privileged users to create and alter logins'
   , START_DATE = '20020101'
   , EXPIRY_DATE = '20300101';
GO

CREATE LOGIN SecurityAdministratorCertificateLogin
    FROM CERTIFICATE SecurityAdministratorCertificate;
GO
GRANT ALTER ANY LOGIN TO SecurityAdministratorCertificateLogin;
GO

--export cert from master
DECLARE @CERTENC VARBINARY(MAX);
DECLARE @CERTPVK VARBINARY(MAX);
SELECT @CERTENC = CERTENCODED(CERT_ID(N'SecurityAdministratorCertificate'));
SELECT @CERTPVK = CERTPRIVATEKEY(CERT_ID(N'SecurityAdministratorCertificate'),
       'All you need is love');

DECLARE @sql nvarchar(MAX);
SELECT @sql = N'CREATE CERTIFICATE SecurityAdministratorCertificate FROM BINARY = '
    + CONVERT(nvarchar(MAX), @CERTENC, 1)
    + ' WITH PRIVATE KEY ( BINARY = '
    + CONVERT(nvarchar(MAX), @CERTPVK, 1)
    + ', DECRYPTION BY PASSWORD = ''All you need is love'');'

--import cert into app databases
USE MyDatabase;

--create database master key, if necessary
IF NOT EXISTS(SELECT 1 FROM sys.symmetric_keys WHERE name = N'##MS_DatabaseMasterKey##')
BEGIN
    CREATE MASTER KEY ENCRYPTION BY PASSWORD='M@sterkEEPassw0rd';
END;

EXEC(@sql);
GO

CREATE PROCEDURE [dbo].[create_login]
    @Login [nvarchar](256),
    @Password [nvarchar](128)
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @Sql NVARCHAR(4000);

    SET @sql = N'CREATE LOGIN ' + QUOTENAME(@Login) + N' WITH PASSWORD = '
        + QUOTENAME(@Password, N'''') + N',CHECK_EXPIRATION=OFF, CHECK_POLICY=ON;';

    EXEC (@sql);

END
GO

--grant exec permissions to users
GRANT EXECUTE ON [dbo].[create_login] TO [my_simple_role];
GO 

--sign proc with certificate
ADD SIGNATURE TO dbo.create_login BY CERTIFICATE SecurityAdministratorCertificate;
GO

编辑:

上面的示例使用数据库主 key 而不是证书特定的密码来加密证书私钥。在数据库恢复或附加到不同 SQL 实例(同时不恢复主数据库)的情况下,您需要重新创建服务器级别 master 数据库中的对象,包括应用程序所需的所有登录名以及存储在 master 中的证书。要恢复证书,一种方法是在恢复/附加后将证书从用户数据库复制到 master,然后重新创建具有权限的证书登录。在这种情况下,DMK 无法自动打开,因为新实例上用于加密数据库主 key 的服务主 key 不同。在将证书复制到 master 数据库的脚本中手动打开 DMK 需要原始密码。用于在数据库之间复制证书的证书密码是临时的,无需保留。

以下是恢复或附加后在 master 中重新创建证书和证书登录的示例:

USE MyDatabase;
--open DMK with original password
OPEN MASTER KEY DECRYPTION BY PASSWORD='M@sterkEEPassw0rd';
--export cert from user database
USE MyDatabase;
DECLARE @CERTENC VARBINARY(MAX);
DECLARE @CERTPVK VARBINARY(MAX);
SELECT @CERTENC = CERTENCODED(CERT_ID(N'SecurityAdministratorCertificate'));
SELECT @CERTPVK = CERTPRIVATEKEY(CERT_ID(N'SecurityAdministratorCertificate'),
       'temporary password here');

DECLARE @sql nvarchar(MAX);
SELECT @sql = N'CREATE CERTIFICATE SecurityAdministratorCertificate FROM BINARY = '
    + CONVERT(nvarchar(MAX), @CERTENC, 1)
    + ' WITH PRIVATE KEY ( BINARY = '
    + CONVERT(nvarchar(MAX), @CERTPVK, 1)
    + ', DECRYPTION BY PASSWORD = ''temporary password here'');'
SELECT @sql
CLOSE MASTER KEY;

--import cert into master
USE master;

--create database master key in new instance master database, if necessary
IF NOT EXISTS(SELECT 1 FROM sys.symmetric_keys WHERE name = N'##MS_DatabaseMasterKey##')
BEGIN
    CREATE MASTER KEY ENCRYPTION BY PASSWORD='M@sterkEEPassw0rd';
END;

EXEC(@sql);
GO

--recreate login and assign permissions
CREATE LOGIN SecurityAdministratorCertificateLogin
    FROM CERTIFICATE SecurityAdministratorCertificate;
GRANT ALTER ANY LOGIN TO SecurityAdministratorCertificateLogin;
GO

关于sql-server - 无法使用 EXECUTE AS OWNER 在存储过程中创建登录名?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33192576/

相关文章:

sql-server - 为什么 UPDATE .WRITE() 仅在我使用 [column].WRITE() 时才有效,而不是 [table].[column].WRITE()?

sql-server - 是否可以在 SQL Server 2008 中使用存储过程作为子查询?

sql-server - 关于多个 LEFT JOIN 性能问题的聚合(SQL SERVER)

sql-server - 负数与 MONTH 日期部分一起使用时的 DATEADD 问题

sql-server - SQL Azure 与 SQL Server 有何不同?

sql-server - 回收时间报告服务

c# - 为模拟 token 调用 DuplicateTokenEx 后仍然出现重复 token 错误

c# - 模拟和 DirectoryEntry

delphi - 我们可以在Delphi中模拟gMSA帐户吗?

sql - 从数据库中提取值 > 1.00