Tyl*_*ing 5 trigger sql-server permissions sql-server-2012
标题是不言自明的,但如果您对我为什么想要这个感到好奇;我想要这个是因为我有一个存档/日志表来存储活动表的过去值,因此我不希望数据有以任何方式受到损害的风险。唯一应该在表上插入的是我在活动表上创建的触发器以记录其更改。在极少数情况下,我们可能需要手动编辑日志表,我将关闭(如果存在)“插入锁”
我将 SQL Server 2012 Enterprise 与 SQL Management Studio 结合使用
Sol*_*zky 10
这可以使用证书和模块签名(即ADD SIGNATURE)来完成。使用 Impersonation viaEXECUTE AS
可能会变得混乱,并且可能会导致其他人冒充“允许的”用户,或更改使用EXECUTE AS
. 但是对于模块签名:无法模拟基于证书的用户(请参阅最终测试用例),在不知道证书密码的情况下无法对另一个模块进行签名,并且如果有人更改了您签名的任何模块(例如触发器),则签名会自动删除,提醒您该更改,然后您可以决定是使用当前更改退出还是拒绝更改;-)。
此外,在触发器中捕获 ApplicationName / ProgramName 并不可靠,因为很容易在 ConnectionString 中传递该值。
请注意,审计表位于不同的架构中 -- Auditing
-- 与主表不同,dbo
以防止所有权链接,假设大多数存储过程也将在dbo
架构中。
设置
USE [...];
GO
CREATE CERTIFICATE [AuditCert]
ENCRYPTION BY PASSWORD = 'Password Goes Here.'
WITH SUBJECT = 'Restrict Insert Test';
GO
CREATE USER [AuditUser]
FROM CERTIFICATE [AuditCert];
-- no DEFAULT_SCHEMA for Certificate-based Users
GO
CREATE SCHEMA [Auditing]
AUTHORIZATION [AuditUser];
GO
-- DROP TABLE [Auditing].[AuditLog];
CREATE TABLE [Auditing].[AuditLog]
(
AuditLogID INT IDENTITY(1, 1) NOT NULL,
AuditDate DATETIME2 NOT NULL
CONSTRAINT [DF_AuditLog_AuditDate] DEFAULT (SYSDATETIME()),
ImportantStuffID INT,
Column2 VARCHAR(50),
CONSTRAINT [PK_AuditLog] PRIMARY KEY CLUSTERED (AuditLogID ASC)
);
GO
CREATE TABLE [dbo].[ImportantStuff]
(
ImportantStuffID INT IDENTITY(1, 1) NOT NULL,
Column2 VARCHAR(50),
CONSTRAINT [PK_ImportantStuff] PRIMARY KEY CLUSTERED (ImportantStuffID ASC)
);
GO
CREATE TRIGGER [dbo].[AuditImportantStuff]
ON [dbo].[ImportantStuff]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [Auditing].[AuditLog] ([ImportantStuffID], [Column2])
SELECT ins.[ImportantStuffID], ins.[Column2]
FROM inserted ins;
END;
GO
ADD SIGNATURE TO [dbo].[AuditImportantStuff]
BY CERTIFICATE [AuditCert]
WITH PASSWORD = 'Password Goes Here.';
GO
CREATE PROCEDURE [dbo].[AttemptDirectInsert]
(
@ImportantStuffID INT,
@Column2 VARCHAR(50)
)
AS
SET NOCOUNT ON;
INSERT INTO [Auditing].[AuditLog] ([ImportantStuffID], [Column2])
VALUES (@ImportantStuffID, @Column2);
GO
CREATE PROCEDURE [dbo].[ImportantStuff_AddData]
(
@ValueForColumn2 VARCHAR(50)
)
AS
SET NOCOUNT ON;
INSERT INTO [dbo].[ImportantStuff] ([Column2])
VALUES (@ValueForColumn2);
GO
CREATE USER [TestUser]
WITHOUT LOGIN
WITH DEFAULT_SCHEMA = [dbo];
GO
GRANT EXECUTE ON [dbo].[AttemptDirectInsert] TO [TestUser];
GRANT EXECUTE ON [dbo].[ImportantStuff_AddData] TO [TestUser];
GO
Run Code Online (Sandbox Code Playgroud)
考试
SELECT SESSION_USER, ORIGINAL_LOGIN();
INSERT INTO [Auditing].[AuditLog] ([ImportantStuffID], [Column2]) VALUES (-1, 'test 1');
EXECUTE AS USER = 'TestUser';
SELECT SESSION_USER, ORIGINAL_LOGIN();
INSERT INTO [Auditing].[AuditLog] ([ImportantStuffID], [Column2]) VALUES (-2, 'test 2');
-- Msg 229, Level 14, State 5, Line 102
-- The INSERT permission was denied on the object 'AuditLog', database '...',
-- schema 'Auditing'.
EXEC [dbo].[AttemptDirectInsert]
@ImportantStuffID = -3,
@Column2 = 'test 3';
-- Msg 229, Level 14, State 5, Procedure AttemptDirectInsert, Line 115
-- The INSERT permission was denied on the object 'AuditLog', database '...',
-- schema 'Auditing'.
INSERT INTO [dbo].[ImportantStuff] ([Column2]) VALUES ('test 4');
-- Msg 229, Level 14, State 5, Line 114
-- The INSERT permission was denied on the object 'ImportantStuff', database '...',
-- schema 'dbo'.
EXEC [dbo].[ImportantStuff_AddData]
@ValueForColumn2 = 'test 5';
-- woo hoo!
SELECT * FROM [Auditing].[AuditLog];
-- Msg 229, Level 14, State 5, Line 122
-- The SELECT permission was denied on the object 'AuditLog', database '...',
-- schema 'Auditing'.
REVERT;
SELECT SESSION_USER, ORIGINAL_LOGIN();
SELECT * FROM [Auditing].[AuditLog];
EXECUTE AS USER = 'AuditUser';
-- Msg 15517, Level 16, State 1, Line 143
-- Cannot execute as the database principal because the principal "AuditUser" does not
-- exist, this type of principal cannot be impersonated, or you do not have permission.
Run Code Online (Sandbox Code Playgroud)
更新
补充说明:
db_owner
固定数据库角色中的任何人都应该能够禁用触发器,并且db_datawriter
如果固定数据库角色中的某个人相当狡猾,那么他们可能至少可以使用 1 个变通方法。TRUSTWORTHY
.EXECUTE AS
更改当前的安全上下文。这基本上等于在说:我登录/用户A,但就目前而言,请使用登录/用户B的权限INSTEAD OF矿。**我确实有大部分完整的示例代码(大约完成了 75%),用于审计表上的触发器,该代码将禁止更新除证书签名的代码之外的任何内容,但没有时间完成它。其概念是在此过程中对证书进行锁定,并且锁定条目包括证书 ID。您可以验证证书 ID 是否是所需的证书,ROLLBACK
如果不是或没有证书在交易中使用。问题VIEW SERVER STATE
是需要使用它来从中创建登录名。然后只授予该登录权限,最后使用相同的证书在审计表上签署触发器(已经在该数据库中,因为它用于在基表上签署触发器)。sys.dm_tran_locks
. 然而,这是一个相当容易解决的问题,因为它可以通过基于证书的登录来授予,甚至可以是相同的证书。在这种情况下,证书可以被备份并恢复到master
VIEW SERVER STATE
如果您喜欢使用EXECUTE AS
(信任可以模拟的用户),另一种方法是:
CREATE TABLE dbo.Test
(
TestID integer IDENTITY PRIMARY KEY,
SomeDate datetime NOT NULL
);
GO
CREATE TABLE dbo.TestArchive
(
TestID integer PRIMARY KEY,
SomeDate datetime NOT NULL
);
Run Code Online (Sandbox Code Playgroud)
-- Ordinary user with the ability to insert to the Test table
CREATE USER NormalUser WITHOUT LOGIN;
GRANT INSERT ON dbo.Test TO NormalUser;
GRANT SHOWPLAN TO NormalUser; -- Not required, for testing only
GO
-- User used by the trigger to move rows to the Archive table
CREATE USER ArchiveUser WITHOUT LOGIN;
GRANT SHOWPLAN TO ArchiveUser; -- Required if normal users have this permission
GO
-- Give ownership of the Archive table to the Archive user
-- to prevent ownership chaining skipping permission checks
ALTER AUTHORIZATION
ON OBJECT::dbo.TestArchive
TO ArchiveUser;
Run Code Online (Sandbox Code Playgroud)
这用于EXECUTE AS
作为 ArchiveUser 执行存档
CREATE TRIGGER dbo_Test_AI
ON dbo.Test
WITH EXECUTE AS 'ArchiveUser'
AFTER INSERT
AS
BEGIN
-- Insert deleted rows
INSERT dbo.TestArchive
(
TestID,
SomeDate
)
SELECT
D.TestID,
D.SomeDate
FROM
(
-- Remove rows ready to be archived
DELETE dbo.Test
OUTPUT Deleted.TestID, Deleted.SomeDate
WHERE SomeDate <= DATEADD(DAY, -7, GETUTCDATE())
) AS D;
END;
Run Code Online (Sandbox Code Playgroud)
EXECUTE AS USER = 'NormalUser';
GO
-- Able to insert Test rows
INSERT dbo.Test (SomeDate)
VALUES
(DATEADD(DAY, -6, GETUTCDATE())),
(DATEADD(DAY, -5, GETUTCDATE())),
(DATEADD(DAY, -4, GETUTCDATE())),
(DATEADD(DAY, -3, GETUTCDATE())),
(DATEADD(DAY, -2, GETUTCDATE())),
(DATEADD(DAY, -1, GETUTCDATE()));
GO
-- Able to insert a Test row that gets archived
INSERT dbo.Test (SomeDate)
VALUES
(DATEADD(DAY, -7, GETUTCDATE()));
GO
-- Not able to insert to the archive directly
INSERT dbo.TestArchive (TestID, SomeDate)
VALUES (100, GETUTCDATE());
GO
REVERT;
Run Code Online (Sandbox Code Playgroud)
DROP TABLE
dbo.Test,
dbo.TestArchive;
DROP USER ArchiveInsert;
DROP USER NormalUser;
Run Code Online (Sandbox Code Playgroud)
请注意,这种安排不会阻止非常有特权的用户直接写入存档表,例如db_datawriter角色的成员或数据库所有者。基于证书的答案也没有。适合您的解决方案完全取决于您当前的权限设置方式、您对各种用户的信任程度以及您的偏执程度。
归档时间: |
|
查看次数: |
2287 次 |
最近记录: |