【发布时间】:2021-11-15 12:12:58
【问题描述】:
我有两个表 T1 和 T2,分别位于两个不同的模式 S1 和 S2 中。我在 T1 上编写了一个触发器 TR1(没有 EXECUTE AS 子句),它将插入(I)、更新(U)和删除(D)记录到 T2 中,T2 具有与 T1 相同的架构,并带有一些额外的元数据列. S1、T1、S2、T2 和 TR1 都归 dbo 所有。
我创建了一个角色 R1,它对 S1(以及 T1)拥有 S、I、U 和 D 权限。该角色还允许 S2(以及 T2)上的 S,但拒绝 I、U 和 D。我创建了一个用户 U1,并为该用户分配了角色 R1。
在 U1 的用户上下文下,如果我在 T2 上尝试 I、U 或 D,则如预期的那样被拒绝。但是,如果将 I、U 或 D 插入 T1,则审计行将成功插入 T2。这是我想要的行为,但想知道这是为什么,因为 U1 已被明确拒绝这些特权。
这是因为所有权链接,因此当 TR1 运行时,U1 的权限永远不会在 T2 上检查,还是其他原因?
Azure SQL 版本是 Microsoft SQL Azure (RTM) - 12.0.2000.8 Jul 23 2021 13:14:19 版权所有 (C) 2019 Microsoft Corporation
--
添加了触发代码:
CREATE TRIGGER TRG ON dbo.T1
FOR INSERT, UPDATE, DELETE
AS
BEGIN;
DECLARE @Operation CHAR(1);
SET @Operation = (
CASE
WHEN EXISTS(SELECT 1 FROM INSERTED) AND EXISTS(SELECT 1 FROM DELETED) THEN 'U'
WHEN EXISTS(SELECT 1 FROM INSERTED) THEN 'I'
WHEN EXISTS(SELECT 1 FROM DELETED) THEN 'D'
ELSE NULL
END
);
IF @Operation = 'I'
BEGIN;
INSERT INTO adt.T1(Operation, ID, C1)
SELECT @Operation, ID, C1
FROM INSERTED;
END;
IF @Operation = 'D'
BEGIN;
INSERT INTO adt.T1 (Operation, ID, C1)
SELECT @Operation, ID, C1
FROM DELETED;
END;
IF @Operation = 'U'
BEGIN;
INSERT INTO adt.T1 (Operation, ID, C1)
SELECT @Operation, i.ID, i.C1
FROM INSERTED i
INNER JOIN DELETED d
ON i.ID = d.ID
-- Hash indicated columns of INSERTED and DELETED to determine if there are any real changes.
WHERE (SELECT HASHBYTES('MD5', (SELECT i.ID, i.C1 FROM (SELECT NULL AS X) t FOR XML AUTO)))
<>
(SELECT HASHBYTES('MD5', (SELECT d.ID, d.C1 FROM (SELECT NULL AS X) t FOR XML AUTO)));
END;
END;
【问题讨论】:
-
也就是说,用户在 T2 上被正确拒绝 IUD 操作,那么为什么同一用户引发的触发器(在另一个表上)能够将记录插入到该表中
-
据我所知,这种情况仅适用于 EXECUTE AS,这就是该子句的重点。请发布您的触发器创建脚本以验证
-
@ChrisSchaller 添加了触发代码
-
@ChrisSchaller 还补充说,表位于不同的模式中,并且权限是在模式上发布的,而不是在表本身上。游戏中的一切都归 dbo 所有。
-
好的,是的,这就是所有权链的作用,因为您在架构级别进行保护。在其他情况下,您可能会考虑直接保护目标表以防止这种行为,但在审计上下文中它很有用,因为我们可以使用当前上下文将当前用户和 privs 记录到审计表中,我们会丢失一些此类信息如果触发器使用 Execute As。对于严格不基于审计的场景,您需要注意所有权链接可能会将受保护的表暴露给您可能没有预料到的更新。
标签: triggers azure-sql-database audit