여기서 인증서는 인증서가 DatabaseA의 저장 프로 시저를 INSERT
두 테이블에 대한 권한 이있는 DatabaseB의 사용자와 연결하는 동안 저장 프로 시저에서 직접 삽입되는 테이블의 트리거가 체인의 다른 모듈이며 획득 한 권한입니다. 인증서에서 체인의 다른 모듈로 전달되지 않습니다. 즉, 인증서는 저장 프로 시저가 사용자를 통해 테이블에 삽입하고 심지어 트리거를 실행할 수 있도록 허용했습니다. 그러나 객체와 관련된 작업을 수행 할 수있는 권한이 트리거에 부여되지 않았습니다 ( 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