默认情况下,DML 和 DDL 触发器在调用触发器的用户的上下文下执行。 触发器的调用方是执行导致触发器运行的语句的用户。 例如,如果用户 Mary 执行导致 DML 触发器 DML_trigMary 运行的 DELETE 语句, 则DML_trigMary 中的代码在 Mary 的用户特权上下文中执行。 想要在数据库或服务器实例中引入恶意代码的用户可以利用此默认行为。 例如,以下 DDL 触发器由用户 JohnDoe
创建:
CREATE TRIGGER DDL_trigJohnDoe
ON DATABASE
FOR ALTER_TABLE
AS
GRANT CONTROL SERVER TO JohnDoe ;
GO
此触发器的含义是,一旦有权执行 GRANT CONTROL SERVER
语句的用户(例如 sysadmin 固定服务器角色的成员)执行 ALTER TABLE
语句 JohnDoe
,就会授予 CONTROL SERVER
权限。 换句话说,虽然 JohnDoe
不能授予 CONTROL SERVER
自己权限,但他启用了触发代码,使他在权限提升的情况下执行这一权限。 DML 和 DDL 触发器都对此类安全威胁开放。
触发安全最佳做法
可以采取以下措施,防止触发代码在提升的权限下执行:
通过查询 sys.triggers 和 sys.server_triggers 目录视图,了解数据库中和服务器实例中存在的 DML 和 DDL 触发器。 以下查询返回当前数据库中的所有 DML 和数据库级 DDL 触发器,以及服务器实例上的所有服务器级 DDL 触发器:
SELECT type, name, parent_class_desc FROM sys.triggers UNION SELECT type, name, parent_class_desc FROM sys.server_triggers ;
使用 DISABLE TRIGGER 禁用触发器,如果触发器在升级的权限下执行,则可能会损害数据库或服务器的完整性。 以下语句禁用当前数据库中的所有数据库级 DDL 触发器:
DISABLE TRIGGER ALL ON DATABASE
此语句禁用服务器实例上的所有服务器级 DDL 触发器:
DISABLE TRIGGER ALL ON ALL SERVER
此语句禁用当前数据库中的所有 DML 触发器:
DECLARE @schema_name sysname, @trigger_name sysname, @object_name sysname ; DECLARE @sql nvarchar(max) ; DECLARE trig_cur CURSOR FORWARD_ONLY READ_ONLY FOR SELECT SCHEMA_NAME(schema_id) AS schema_name, name AS trigger_name, OBJECT_NAME(parent_object_id) as object_name FROM sys.objects WHERE type in ('TR', 'TA') ; OPEN trig_cur ; FETCH NEXT FROM trig_cur INTO @schema_name, @trigger_name, @object_name ; WHILE @@FETCH_STATUS = 0 BEGIN SELECT @sql = 'DISABLE TRIGGER ' + QUOTENAME(@schema_name) + '.' + QUOTENAME(@trigger_name) + ' ON ' + QUOTENAME(@schema_name) + '.' + QUOTENAME(@object_name) + ' ; ' ; EXEC (@sql) ; FETCH NEXT FROM trig_cur INTO @schema_name, @trigger_name, @object_name ; END GO -- Verify triggers are disabled. Should return an empty result set. SELECT * FROM sys.triggers WHERE is_disabled = 0 ; GO CLOSE trig_cur ; DEALLOCATE trig_cur;