トリガのセキュリティの管理
既定では、DML トリガも DDL トリガも、トリガを呼び出したユーザーのコンテキストで実行されます。トリガの呼び出し元は、トリガが実行される原因となったステートメントを実行したユーザーです。たとえば、ユーザー Mary が DELETE ステートメントを実行し、このために DML トリガ DML_trigMary が実行された場合、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;