带有签名过程的 SQL Server Service Broker - 权限问题

问题描述 投票:0回答:1

我们已在启用内部激活的情况下运行 Service Broker。存储过程已签名。这个权限问题是如何发生的?我认为签名用户在调用过程时接管。为什么权限问题与签名用户无关?

916:服务器主体“{userName}”无法在当前安全上下文下访问数据库“{sisterDatabaseName}”。 - 线路:1

我们在同一台服务器上有多个数据库。这些数据库成对分组,数据库偶尔需要来回发送信息。通常,只需在数据库之间执行选择或插入时完全限定数据库名称即可完成此操作。

例如:

SELECT *
FROM dbname.dbo.tablename

但是,我们有一个流程可以从异步 SQL 执行中受益匪浅。

进入服务代理!我们运行了以下脚本:

ALTER AUTHORIZATION ON database::databasename TO someuser
ALTER DATABASE databasename SET NEW_BROKER WITH ROLLBACK IMMEDIATE
DECLARE @Password VARCHAR(20) = ''
DECLARE @char CHAR = ''
DECLARE @charI INT = 0
DECLARE @len INT = 20 -- Length of Password
WHILE LEN(@Password) < @len
BEGIN
    SET @charI = ROUND(RAND()*73,0) + 49
    SET @char = CHAR(@charI)
    SET @Password += @char
END

SET @SQL = '
CREATE CERTIFICATE {certName}
ENCRYPTION BY PASSWORD = ''' + @Password + '''
WITH SUBJECT = ''Certificate to sign Internal Activation Procedure''

CREATE USER {certificateUserName} FROM CERTIFICATE {certName};

GRANT CONNECT TO PullConfirmRequestQueueProcessorUser;

ADD SIGNATURE TO [dbo].[{procedureName}]
BY CERTIFICATE {certName} WITH PASSWORD = ''' + @Password + '''
'

EXEC(@SQL)

本质上,我们正在创建一个只能分配给这个特定存储过程的证书。现在还好。这是在测试阶段。如果我们需要一种可以扩展到多个程序的更安全的解决方案,我们可以稍后再提出。我并不担心。但是,我希望这个测试能够发挥作用。

这是存储过程:

IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[{procedureName}]') AND type in (N'P', N'PC'))
BEGIN
    EXEC dbo.sp_executesql @statement = N'CREATE PROCEDURE [dbo].[{procedureName}] AS' 
    GRANT EXECUTE ON {procedureName} TO {username}
END
GO

ALTER PROCEDURE [dbo].[{procedureName}]
WITH EXECUTE AS OWNER
AS
BEGIN
    SET NOCOUNT ON

    BEGIN TRY
        DECLARE @SSBSTargetDialogHandle UNIQUEIDENTIFIER;
        DECLARE @RecvdRequestMessage XML;
        DECLARE @RecvdRequestMessageTypeName sysname;
        WHILE (1=1)
        BEGIN
            BEGIN TRANSACTION;
            WAITFOR
            ( RECEIVE TOP(1)
            @SSBSTargetDialogHandle = conversation_handle,
            @RecvdRequestMessage = CONVERT(XML, message_body),
            @RecvdRequestMessageTypeName = message_type_name
            FROM dbo.{queueName}
            ), TIMEOUT 1000;

            IF (@@ROWCOUNT = 0)
            BEGIN
                IF (@@TRANCOUNT > 0 ) ROLLBACK TRANSACTION;
                BREAK;
            END

            IF @RecvdRequestMessageTypeName = N'{messageName}'
            BEGIN
                --Statements to process message in this database and "sister" database

                END CONVERSATION @SSBSTargetDialogHandle;
            END
            ELSE IF @RecvdRequestMessageTypeName IN (N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog',N'http://schemas.microsoft.com/SQL/ServiceBroker/Error')
            BEGIN
                END CONVERSATION @SSBSTargetDialogHandle;
            END

            COMMIT TRANSACTION;
        END
    END TRY
    BEGIN CATCH
        IF (@@TRANCOUNT > 0 ) ROLLBACK TRANSACTION;
        INSERT INTO ServiceBrokerErrors(
            MessageXml,
            DialogHandle,
            TimeLoggedUTC,
            ErrorNumber,
            ErrorSeverity,
            ErrorState,
            ErrorProcedure,
            ErrorLine,
            ErrorMessage)
        VALUES(@RecvdRequestMessage,@SSBSTargetDialogHandle,GETUTCDATE(),ERROR_NUMBER(),ERROR_SEVERITY(),ERROR_STATE(),ERROR_PROCEDURE(),ERROR_LINE(),ERROR_MESSAGE())
    END CATCH
END
GO

以下四个查询返回相同的值:

SELECT sid FROM {mainDatabase}.sys.database_principals WHERE name = 'dbo'
SELECT sid FROM {sisterDatabase}.sys.database_principals WHERE name = 'dbo'
SELECT owner_sid FROM sys.databases WHERE name = '{mainDatabase}'
SELECT owner_sid FROM sys.databases WHERE name = '{sisterDatabase}'
sql-server user-permissions service-broker
1个回答
1
投票

对于跨数据库访问,需要将证书复制到签名过程访问的每个数据库中。将证书复制到其他数据库的示例脚本:

USE {mainDatabase};
DECLARE @cert_id int = cert_id('{certName}')
DECLARE @public_key  varbinary(MAX) = certencoded(@cert_id),
        @private_key varbinary(MAX) =
           certprivatekey(@cert_id,
              '{password}',
              '{password}');

DECLARE @sql nvarchar(MAX) =
      'CREATE CERTIFICATE {certName}
       FROM  BINARY = ' + convert(varchar(MAX), @public_key, 1) + '
       WITH PRIVATE KEY (BINARY = ' +
          convert(varchar(MAX), @private_key, 1) + ',
          DECRYPTION BY PASSWORD = ''{password}'',
          ENCRYPTION BY PASSWORD = ''{password}'')';

EXEC {sisterDatabase}.sys.sp_executesql @sql;
© www.soinside.com 2019 - 2024. All rights reserved.