다음을 통해 공유


트리거 보안 관리

적용 대상: SQL Server Azure SQL 데이터베이스 Azure SQL Managed Instance

기본적으로 두 DML 및 DDL 트리거는 트리거를 호출하는 사용자의 컨텍스트에서 모두 실행됩니다. 트리거 호출자는 트리거를 실행시키는 문을 실행하는 사용자입니다. 예를 들어 Mary 라는 사용자가 DML 트리거 DML_trigMary 를 실행시키는 DELETE 문을 실행하면 DML_trigMary 내에 있는 코드는 Mary에 대한 사용자 권한 컨텍스트에서 실행됩니다. 이 기본 동작은 데이터베이스 또는 서버 인스턴스에 악성 코드를 도입하려는 사용자가 악용할 수 있습니다. 예를 들어 다음 DDL 트리거는 JohnDoe라는 사용자가 만든 것입니다.

CREATE TRIGGER DDL_trigJohnDoe
ON DATABASE
FOR ALTER_TABLE
AS
SET NOCOUNT ON;

BEGIN TRY
  EXEC(N'
    USE [master];
    GRANT CONTROL SERVER TO [JohnDoe];
');
END TRY
BEGIN CATCH
  DECLARE @DoNothing INT;
END CATCH;
GO

이 트리거의 의미는 sysadmin 고정 서버 역할의 멤버와 같이 GRANT CONTROL SERVER 문을 실행할 수 있는 권한이 있는 사용자가 ALTER TABLE 문을 실행하는 즉시 JohnDoeCONTROL SERVER 사용 권한이 부여된다는 의미입니다. 즉, JohnDoe는 자신에게 CONTROL SERVER 권한을 부여할 수 없지만 에스컬레이션된 권한으로 실행할 수 있는 권한을 부여하는 트리거 코드를 사용하도록 설정했습니다. DML 및 DDL 트리거는 모두 이러한 종류의 보안 위협에 열려 있습니다.

사용자 보안 모범 사례 트리거

트리거 코드가 에스컬레이션된 권한으로 실행되지 않도록 하기 위해 다음 조치를 취할 수 있습니다.

  • sys.triggerssys.server_triggers 카탈로그 뷰를 쿼리하여 데이터베이스와 서버 인스턴스에 있는 DML 및 DDL 트리거를 확인합니다. 다음 쿼리는 현재 데이터베이스의 모든 DML 및 데이터베이스 수준 DDL 트리거와 서버 인스턴스의 모든 서버 수준 DDL 트리거를 반환합니다.

    SELECT type, name, parent_class_desc FROM sys.triggers
    UNION ALL
    SELECT type, name, parent_class_desc FROM sys.server_triggers;
    

    참고 항목

    Azure SQL Managed Instance를 사용하지 않는 경우 Azure SQL 데이터베이스에 sys.triggers만 사용할 수 있습니다.

  • sys.triggers 카탈로그 뷰를 쿼리하여 데이터베이스에 있는 DML 및 DDL 트리거를 확인합니다. 다음 쿼리는 현재 데이터베이스의 모든 DML 및 데이터베이스 수준 DDL 트리거를 반환합니다.

    SELECT type, name, parent_class_desc FROM sys.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 = N'DISABLE TRIGGER ' + QUOTENAME(@schema_name) + N'.'
            + QUOTENAME(@trigger_name)
            + N' ON ' + QUOTENAME(@schema_name) + N'.'
            + QUOTENAME(@object_name) + N'; ';
        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;