트리거 보안 관리
적용 대상: 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
문을 실행하는 즉시 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 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;