既定では、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
このトリガーは、sysadmin 固定サーバー ロールのメンバーなどのGRANT CONTROL SERVER
ステートメントを実行する権限を持つユーザーが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;