我使用跨数据库证书(如 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
这里的问题是,虽然证书将 DatabaseA 中的存储过程与 DatabaseB 中
INSERT
对这两个表具有权限的用户链接起来,但从存储过程直接插入的表上的触发器是链中的另一个模块,并且获得了权限from 证书不会传递给链中的其他模块。意思是,证书允许存储过程通过用户插入到表中,甚至执行触发器。但是,没有授予触发器执行与对象相关的任何操作的权限(执行类似的操作SELECT 1;
)。在这种情况下,需要通过相同的证书向触发器授予权限,以便它可以采取任何必要的操作。这至少可以通过对触发器进行会签来完成。你可以通过执行
ADD COUNTER SIGNATURE TO [TriggerSchema].[TriggerName] BY CERTIFICATE ...;
. 之后,它应该可以正常工作,即使INSERT
触发器插入的表上的基于证书的用户没有直接许可。下面的示例代码重现了该问题,通过添加计数器签名解决了该问题,但不授予
INSERT
触发器填充表的权限。清理
设置
测试 1:触发器失败
测试 2:触发成功
请注意,所做的唯一更改是
ADD COUNTER SIGNATURE
;没有GRANT INSERT ON dbo.TablePopulatedByTrigger TO [PermissionsUser];
。