Quyền kích hoạt khi sử dụng chứng chỉ cơ sở dữ liệu chéo


10

Tôi sử dụng chứng chỉ cơ sở dữ liệu chéo ( như được giải thích bởi Erland Sommarskog ) để kiểm soát quyền truy cập vào một cơ sở dữ liệu nhất định trong môi trường của tôi (SQL Server 2008 R2).

Tôi đã lưu trữ các thủ tục trong cơ sở dữ liệu A cập nhật các bảng trong cơ sở dữ liệu B. Điều này luôn hoạt động đối với nhiều quy trình được lưu trữ trong db A và các bảng trong db B cho đến bây giờ. Tôi đang cố gắng cập nhật một bảng trong db B, nhưng bảng có một kích hoạt trên đó. Trình kích hoạt này đang chèn dữ liệu bổ sung vào một bảng khác trong db B. Tôi đang gặp lỗi:

Msg 916, Cấp 14, Trạng thái 1, Bảng thủ tục_trình, Dòng 11 Máy chủ chính "sql \ login" không thể truy cập cơ sở dữ liệu "B" trong bối cảnh bảo mật hiện tại.

Tôi đã thử cấp quyền chèn cho người dùng cơ sở dữ liệu B được gắn với chứng chỉ để chèn vào bảng khác, nhưng nó không giải quyết được lỗi. Tôi có bất kỳ tùy chọn nào ngoài việc thay đổi kích hoạt để nó sử dụng WITH EXECUTE AS OWNERkhông?

Đây là DDL để nhân rộng vấn đề:

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

Câu trả lời:


8

Vấn đề ở đây là trong khi Chứng chỉ liên kết thủ tục được lưu trữ trong DatabaseA với Người dùng trong DatabaseB có INSERTquyền trên hai bảng, thì Trình kích hoạt trên bảng được chèn trực tiếp từ thủ tục được lưu trữ là một mô-đun khác trong chuỗi và có được quyền từ Chứng chỉ không chuyển qua các mô-đun khác trong chuỗi. Có nghĩa là, Chứng chỉ cho phép Thủ tục lưu trữ chèn vào bảng thông qua Người dùng và thậm chí thực thi Kích hoạt. Nhưng, không có quyền nào được trao cho Trình kích hoạt để làm bất cứ điều gì liên quan đến các đối tượng (làm việc gì đó như SELECT 1;sẽ hoạt động).

Trong trường hợp này, các quyền cần phải được trao cho Người kích hoạt, thông qua cùng một Chứng chỉ đó, để nó có thể thực hiện bất kỳ hành động nào là cần thiết. Điều này có thể được thực hiện, ít nhất, bằng cách ký ngược lại Kích hoạt. Và bạn làm điều đó bằng cách thực hiện ADD COUNTER SIGNATURE TO [TriggerSchema].[TriggerName] BY CERTIFICATE ...;. Sau đó, nó sẽ hoạt động ngay cả khi không có sự INSERTcho phép trực tiếp của Người dùng dựa trên Chứng chỉ trên bảng được Trình kích hoạt chèn vào.

Mã ví dụ dưới đây tái tạo vấn đề, khắc phục sự cố bằng cách thêm chữ ký truy cập, nhưng không cấp INSERTquyền cho bảng kích hoạt.

DỌN DẸP

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;

THIẾT LẬP

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

KIỂM TRA 1: Kích hoạt thất bại

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

KIỂM TRA 2: Kích hoạt thành công

Xin lưu ý rằng thay đổi duy nhất đang được thực hiện là ADD COUNTER SIGNATURE; không có là 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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.