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

时间:2015-10-17 23:10:02

标签: sql-server impersonation sql-server-2014

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

  

Msg 15247,Level 16,State 1,Line 25用户无权访问   执行此操作。

为什么?!如果我在程序中选择了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! :(

如果我在外部运行语句,那么它就可以运行。

我尝试制作一个允许普通用户添加服务器登录的存储过程。以上似乎不起作用。请指教!

1 个答案:

答案 0 :(得分:1)

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

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

  1. 在主数据库中创建证书
  2. 为该证书创建登录信息
  3. 授予证书登录权限以创建登录
  4. 从主人
  5. 导出证书
  6. 将证书导入应用程序数据库
  7. 使用证书
  8. 对存储过程进行签名

    以下是从Erland Sommarskog's web site收集的示例。请注意,每次更改时,您都需要使用证书重新签署proc。

    --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
    

    修改

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

    以下是在还原或附加后在master中重新创建cert和cert登录的示例:

    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