สิทธิ์ในทริกเกอร์เมื่อใช้ใบรับรองฐานข้อมูลข้าม


10

ฉันใช้ใบรับรองฐานข้อมูลข้าม ( ตามที่อธิบายโดย Erland Sommarskog ) เพื่อควบคุมการเข้าถึงฐานข้อมูลบางอย่างในสภาพแวดล้อมของฉัน (SQL Server 2008 R2)

ฉันมีโพรซีเดอร์ที่เก็บไว้ในฐานข้อมูล A ซึ่งอัพเดตตารางในฐานข้อมูล B. มันใช้ได้กับโพรซีเดอร์ที่เก็บไว้ในฐานข้อมูล A และตารางในฐานข้อมูล 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

คำตอบ:


8

ปัญหาคือที่นี่ในขณะที่ใบรับรองเชื่อมโยงกระบวนงานที่เก็บไว้ใน DatabaseA กับผู้ใช้ใน DatabaseB ที่มีINSERTสิทธิ์ในตารางที่สองทริกเกอร์ในตารางที่ถูกแทรกลงในโดยตรงจากกระบวนงานที่เก็บไว้เป็นโมดูลอื่นในห่วงโซ่และสิทธิ์ที่ได้รับ จาก Certificates ไม่ผ่านไปยังโมดูลอื่น ๆ ในกลุ่ม ความหมายใบรับรองอนุญาตขั้นตอนการจัดเก็บเพื่อแทรกลงในตารางผ่านทางผู้ใช้และแม้กระทั่งเรียกใช้ทริกเกอร์ แต่ไม่มีสิทธิ์ได้รับมอบให้กับทริกเกอร์ที่จะทำอะไรที่เกี่ยวข้องกับวัตถุ (ทำสิ่งที่ต้องการSELECT 1;ทำงานหากว่า)

ในกรณีนี้จำเป็นต้องให้สิทธิ์แก่ Trigger ผ่านใบรับรองเดียวกันนั้นเพื่อให้สามารถดำเนินการใด ๆ ที่จำเป็น สิ่งนี้สามารถเกิดขึ้นได้อย่างน้อยที่สุดโดยการลงนามตอบโต้ 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.