AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 161037
Accepted
Dave.Gugg
Dave.Gugg
Asked: 2017-01-14 14:39:03 +0800 CST2017-01-14 14:39:03 +0800 CST 2017-01-14 14:39:03 +0800 CST

使用跨数据库证书时触发器中的权限

  • 772

我使用跨数据库证书(如 Erland Sommarskog 所解释的)来控制对我的环境(SQL Server 2008 R2)中某个数据库的访问。

我在数据库 A 中存储了更新数据库 B 中表的存储过程。到目前为止,这一直适用于 db A 中的各种存储过程和 db B 中的表。我正在尝试更新 db B 中的表,但该表上有一个触发器。此触发器正在 db B 的另一个表中插入其他数据。我收到错误消息:

消息 916,级别 14,状态 1,过程 table_trigger,第 11 行 服务器主体“sql\login”无法在当前安全上下文下访问数据库“B”。

我尝试为与证书绑定的数据库 B 用户授予插入权限以插入到另一个表中,但它没有解决错误。除了更改触发器以使其使用之外,我还有其他选择WITH EXECUTE AS OWNER吗?

这是复制问题的 DDL:

CREATE LOGIN [GuggTest] WITH PASSWORD=N'abcd', DEFAULT_DATABASE=[master], CHECK_EXPIRATION=OFF, CHECK_POLICY=OFF

CREATE DATABASE A;
CREATE DATABASE B;

USE A;

CREATE TABLE dbo.SPtoUpdate
    (
      ID INT
    , ILoveFishing VARCHAR(255)
    );
INSERT INTO dbo.SPtoUpdate
        ( ID , ILoveFishing )
VALUES  ( 1,'Musky'),( 2,'Pike'),( 3,'Yellow Perch');
CREATE TABLE dbo.TriggerToInsert
    (
      ID INT
    , ILoveFishing VARCHAR(255)
    , ChangeDate DATETIME2
    );
GO

CREATE TRIGGER dbo.SPtoUpdateTrigger ON dbo.SPtoUpdate
    FOR UPDATE
AS
    DECLARE @datetime DATETIME2;
    SELECT  @datetime = GETDATE()

    INSERT  INTO dbo.TriggerToInsert
            ( ID , ILoveFishing , ChangeDate )
    VALUES  ( 1 , 'Yes' , @datetime );
GO

CREATE CERTIFICATE BExecutor
   ENCRYPTION BY PASSWORD = 'Obfuscated'
   WITH SUBJECT = 'Execute sp from B to A',
   START_DATE = '20140101', EXPIRY_DATE = '20300101'
GO

BACKUP CERTIFICATE BExecutor TO FILE = 'C:\temp\crossdbcert.cer'
WITH PRIVATE KEY (FILE = 'C:\temp\crossdbcert.pvk' ,
                  ENCRYPTION BY PASSWORD = 'Obfuscated',
                  DECRYPTION BY PASSWORD = 'Obfuscated')
GO

CREATE USER BExecutor FROM CERTIFICATE BExecutor

GRANT UPDATE ON dbo.SPtoUpdate TO BExecutor
GRANT SELECT ON dbo.SPtoUpdate TO BExecutor
--Also give insert on dbo.TriggerToInsert
GRANT INSERT ON dbo.TriggerToInsert TO BExecutor

USE B
GO

CREATE USER [GuggTest] FOR LOGIN [GuggTest];
EXEC sp_addrolemember N'db_owner', N'GuggTest'
GO

CREATE PROCEDURE dbo.UpdateTableInA
AS
    BEGIN
        UPDATE  A.dbo.SPtoUpdate
        SET     ILoveFishing = 'Walleye'
        WHERE   ID = 2;
    END

GO


CREATE CERTIFICATE BExecutor FROM FILE = 'C:\temp\crossdbcert.cer'
WITH PRIVATE KEY (FILE = 'C:\temp\crossdbcert.pvk' ,
                  ENCRYPTION BY PASSWORD = 'Obfuscated',
                  DECRYPTION BY PASSWORD = 'Obfuscated')
GO

EXEC master..xp_cmdshell 'DEL C:\temp\crossdbcert.*', 'no_output'
GO

ADD SIGNATURE TO dbo.UpdateTableInA BY CERTIFICATE BExecutor
    WITH PASSWORD = 'Obfuscated'
GO

--Log In or Change execution context to GuggTest, then EXEC dbo.UpdateTableInA
sql-server permissions
  • 1 1 个回答
  • 2468 Views

1 个回答

  • Voted
  1. Best Answer
    Solomon Rutzky
    2017-01-19T09:25:02+08:002017-01-19T09:25:02+08:00

    这里的问题是,虽然证书将 DatabaseA 中的存储过程与 DatabaseB 中INSERT对这两个表具有权限的用户链接起来,但从存储过程直接插入的表上的触发器是链中的另一个模块,并且获得了权限from 证书不会传递给链中的其他模块。意思是,证书允许存储过程通过用户插入到表中,甚至执行触发器。但是,没有授予触发器执行与对象相关的任何操作的权限(执行类似的操作SELECT 1;)。

    在这种情况下,需要通过相同的证书向触发器授予权限,以便它可以采取任何必要的操作。这至少可以通过对触发器进行会签来完成。你可以通过执行ADD COUNTER SIGNATURE TO [TriggerSchema].[TriggerName] BY CERTIFICATE ...;. 之后,它应该可以正常工作,即使INSERT触发器插入的表上的基于证书的用户没有直接许可。

    下面的示例代码重现了该问题,通过添加计数器签名解决了该问题,但不授予INSERT触发器填充表的权限。

    清理

    USE [master];
    GO
    IF EXISTS (SELECT 1 FROM [sys].[databases] WHERE [name] = N'DatabaseA')
    BEGIN
        PRINT 'Dropping [DatabaseA] DB...';
        ALTER DATABASE [DatabaseA] SET OFFLINE WITH ROLLBACK IMMEDIATE;
        ALTER DATABASE [DatabaseA] SET ONLINE;
        DROP DATABASE [DatabaseA];
    END;
    
    IF EXISTS (SELECT 1 FROM [sys].[databases] WHERE [name] = N'DatabaseB')
    BEGIN
        PRINT 'Dropping [DatabaseB] DB...';
        ALTER DATABASE [DatabaseB] SET OFFLINE WITH ROLLBACK IMMEDIATE;
        ALTER DATABASE [DatabaseB] SET ONLINE;
        DROP DATABASE [DatabaseB];
    END;
    
    IF (SUSER_ID(N'JohnnyLunchbucket') IS NOT NULL)
    BEGIN
      PRINT 'Dropping [JohnnyLunchbucket] Login...';
      DROP LOGIN [JohnnyLunchbucket];
    END;
    
    IF (OBJECT_ID(N'tempdb..#CertInfo') IS NOT NULL)
    BEGIN
      PRINT 'Dropping [#CertInfo] Temp Table...';
      DROP TABLE #CertInfo;
    END;
    

    设置

    USE [master];
    
    EXECUTE AS LOGIN = N'sa';
    PRINT 'Creating databases...';
    CREATE DATABASE [DatabaseA] COLLATE Latin1_General_100_CI_AS_SC;
    CREATE DATABASE [DatabaseB] COLLATE Latin1_General_100_CI_AS_SC;
    REVERT;
    GO
    
    -- Default for both options should be OFF, but just to be sure:
    ALTER DATABASE [DatabaseA] SET DB_CHAINING OFF;
    ALTER DATABASE [DatabaseA] SET TRUSTWORTHY OFF;
    
    ALTER DATABASE [DatabaseB] SET DB_CHAINING OFF;
    ALTER DATABASE [DatabaseB] SET TRUSTWORTHY OFF;
    GO
    
    CREATE LOGIN [JohnnyLunchbucket] WITH PASSWORD = 'OhSoSecure;)';
    
    
    USE [DatabaseA];
    
    CREATE USER [JohnnyLunchbucket] FOR LOGIN [JohnnyLunchbucket];
    GO
    
    --DROP PROCEDURE dbo.InsertIntoTableWithoutTrigger;
    CREATE PROCEDURE dbo.InsertIntoTableWithoutTrigger
    (
        @SomeValue NVARCHAR(50)
    )
    AS
    SET NOCOUNT ON;
    
    INSERT INTO [DatabaseB].[dbo].[TableWithoutTrigger] (SomeValue)
    VALUES (@SomeValue);
    GO
    
    GRANT EXECUTE ON dbo.InsertIntoTableWithoutTrigger TO [JohnnyLunchbucket];
    GO
    
    CREATE PROCEDURE dbo.InsertIntoTableWithTrigger
    AS
    SET NOCOUNT ON;
    
    INSERT INTO [DatabaseB].[dbo].[TableWithTrigger] (SomeOtherValue)
    VALUES (NEWID());
    GO
    
    GRANT EXECUTE ON dbo.InsertIntoTableWithTrigger TO [JohnnyLunchbucket];
    
    
    
    CREATE CERTIFICATE [PermissionsCert]
      AUTHORIZATION [dbo]
      ENCRYPTION BY PASSWORD = 'WeakPassword'
      WITH SUBJECT = 'Used to test granting permissions to code',
      EXPIRY_DATE = '2099-12-31';
    
    ADD SIGNATURE TO [dbo].[InsertIntoTableWithoutTrigger]
        BY CERTIFICATE [PermissionsCert]
        WITH PASSWORD = 'WeakPassword';
    
    ADD SIGNATURE TO [dbo].[InsertIntoTableWithTrigger]
        BY CERTIFICATE [PermissionsCert]
        WITH PASSWORD = 'WeakPassword';
    
    -- Save Certificate info in temporary table so we can recreate in DatabaseB
    SELECT CERTENCODED(CERT_ID(N'PermissionsCert')) AS [PublicKey],
           CERTPRIVATEKEY(CERT_ID(N'PermissionsCert'), 'OtherPassword', 'WeakPassword')
                  AS [PrivateKey]
    INTO   #CertInfo;
    GO
    
    USE [DatabaseB];
    
    DECLARE @SQL NVARCHAR(MAX);
    
    SELECT @SQL = N'CREATE CERTIFICATE [PermissionsCert] AUTHORIZATION [dbo] FROM BINARY = '
                   + CONVERT(NVARCHAR(MAX), [PublicKey], 1)
                   + N' WITH PRIVATE KEY (BINARY = '
                   + CONVERT(NVARCHAR(MAX), [PrivateKey], 1)
                   + N', DECRYPTION BY PASSWORD = N''OtherPassword'''
                   + N', ENCRYPTION BY PASSWORD = ''WeakPassword'');'
    FROM   #CertInfo;
    
    PRINT @SQL;
    EXEC (@SQL);
    
    CREATE USER [PermissionsUser] FROM CERTIFICATE [PermissionsCert];
    
    --DROP TABLE dbo.[TableWithoutTrigger];
    CREATE TABLE dbo.[TableWithoutTrigger]
    (
      [TableWithoutTriggerID] INT NOT NULL IDENTITY(1, 1)
         CONSTRAINT [PK_TableWithoutTrigger] PRIMARY KEY,
      [SomeValue] NVARCHAR(50)
    );
    
    GRANT INSERT ON [dbo].[TableWithoutTrigger] TO [PermissionsUser];
    
    
    CREATE TABLE dbo.[TableWithTrigger]
    (
      [TableWithTriggerID] INT NOT NULL IDENTITY(1, 1)
         CONSTRAINT [PK_TableWithTrigger] PRIMARY KEY,
      [SomeOtherValue] NVARCHAR(50)
    );
    
    GRANT INSERT ON [dbo].[TableWithTrigger] TO [PermissionsUser];
    
    
    CREATE TABLE dbo.[TablePopulatedByTrigger]
    (
      [TablePopulatedByTriggerID] INT NOT NULL IDENTITY(1, 1)
         CONSTRAINT [PK_TablePopulatedByTrigger] PRIMARY KEY,
      [DuplicatedValue] NVARCHAR(50)
    );
    GO
    
    CREATE TRIGGER dbo.CopySomeOtherValue
    ON dbo.[TableWithTrigger]
    AFTER INSERT
    AS
    BEGIN
        SET NOCOUNT ON;
    
        INSERT INTO dbo.[TablePopulatedByTrigger] ([DuplicatedValue])
            SELECT ins.[SomeOtherValue]
            FROM   inserted ins;
    END;
    GO
    

    测试 1:触发器失败

    USE [DatabaseA];
    
    EXECUTE AS LOGIN = 'JohnnyLunchbucket';
    SELECT SESSION_USER AS [User], ORIGINAL_LOGIN() AS [OriginalLogin];
    GO
    
    SELECT * FROM [DatabaseB].[dbo].[TableWithoutTrigger];
    SELECT * FROM [DatabaseB].[dbo].[TableWithTrigger];
    SELECT * FROM [DatabaseB].[dbo].[TablePopulatedByTrigger];
    INSERT INTO [DatabaseB].[dbo].[TableWithoutTrigger] ([SomeValue]) VALUES (N'test 0');
    USE [DatabaseB];
    /* -- All 5 statements above get the following error:
    Msg 916, Level 14, State 1, Line xxxxxx
    The server principal "JohnnyLunchbucket" is not able to access the database
        "DatabaseB" under the current security context.
    */
    
    
    EXEC [dbo].[InsertIntoTableWithoutTrigger] @SomeValue = N'test A'; -- SUCCESS!!!
    
    EXEC [dbo].[InsertIntoTableWithTrigger]; -- ERROR:
    /*
    Msg 916, Level 14, State 1, Procedure CopySomeOtherValue, Line xxxxxx
    The server principal "JohnnyLunchbucket" is not able to access the database
        "DatabaseB" under the current security context.
    */
    
    REVERT;
    SELECT SESSION_USER AS [User], ORIGINAL_LOGIN() AS [OriginalLogin];
    
    -- Check to make sure that dbo.InsertIntoTableWithoutTrigger really did work:
    SELECT * FROM [DatabaseB].[dbo].[TableWithoutTrigger];
    -- 1    test A
    

    测试 2:触发成功

    请注意,所做的唯一更改是ADD COUNTER SIGNATURE;没有GRANT INSERT ON dbo.TablePopulatedByTrigger TO [PermissionsUser];。

    USE [DatabaseB];
    
    ADD COUNTER SIGNATURE
        TO dbo.[CopySomeOtherValue]
        BY CERTIFICATE [PermissionsCert]
        WITH PASSWORD = 'WeakPassword';
    GO
    
    
    USE [DatabaseA];
    
    EXECUTE AS LOGIN = 'JohnnyLunchbucket';
    SELECT SESSION_USER AS [User], ORIGINAL_LOGIN() AS [OriginalLogin];
    GO
    
    INSERT INTO [DatabaseB].[dbo].[TableWithTrigger] ([SomeOtherValue]) VALUES (N'Test B');
    INSERT INTO [DatabaseB].[dbo].[TablePopulatedByTrigger]([DuplicatedValue]) VALUES ('Test B')
    /*
    Msg 916, Level 14, State 1, Line xxxxxx
    The server principal "JohnnyLunchbucket" is not able to access the database
        "DatabaseB" under the current security context.
    */
    
    
    EXEC [dbo].[InsertIntoTableWithTrigger]; -- SUCCESS!!!
    
    REVERT;
    SELECT SESSION_USER AS [User], ORIGINAL_LOGIN() AS [OriginalLogin];
    
    SELECT * FROM [DatabaseB].[dbo].[TableWithTrigger];
    SELECT * FROM [DatabaseB].[dbo].[TablePopulatedByTrigger];
    -- 2    968DB092-C3DE-4E4B-92B9-E21CA551A5FA
    -- 1    968DB092-C3DE-4E4B-92B9-E21CA551A5FA
    
    • 8

相关问题

  • SQL Server - 使用聚集索引时如何存储数据页

  • 我需要为每种类型的查询使用单独的索引,还是一个多列索引可以工作?

  • 什么时候应该使用唯一约束而不是唯一索引?

  • 死锁的主要原因是什么,可以预防吗?

  • 如何确定是否需要或需要索引

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve