共用方式為


管理觸發程序安全性

適用於:SQL Server Azure SQL 資料庫 Azure SQL 受控執行個體

依預設,DML 與 DDL 觸發程序會在呼叫觸發程序的該使用者之環境下執行。 觸發程序的呼叫者是執行陳述式而引發觸發程序執行的使用者。 例如,如果使用者 Mary 執行 DELETE 陳述式而引發 DML 觸發程序 DML_trigMary 執行,就會在 Mary 使用者權限的環境下執行 DML_trigMary內的程式碼。 此預設行為有可能遭到居心不良的使用者加以利用,成為在資料庫或伺服器執行個體中導入惡意程式碼的漏洞。 例如,下列 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

這個觸發程序所代表的意義是,只要有權執行 GRANT CONTROL SERVER 陳述式的使用者 (例如 sysadmin 固定伺服器角色的成員) 執行 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 受控執行個體,否則 Azure SQL Database 只能使用 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;