Finden Sie die Identität des Clients, der eine Abfrage in SQL Server auslöst, ohne Trigger zu verwenden?

11

Ich verwende derzeit Change Data Capture (CDC) , um Datenänderungen zu verfolgen, und ich möchte den Hostnamen und die IP-Adresse des Clients verfolgen, der die Abfrage sendet, die die Änderungen vorgenommen hat. Wenn 5 verschiedene Clients über denselben Benutzernamen angemeldet sind, besteht das Rätsel, welcher der 5 Clients die Abfrage ausgelöst hat. Andere spezielle Lösungen, die ich gefunden habe, umfassen das Ändern der CDC-Tabelle mit dem folgenden Befehl:

ALTER TABLE cdc.schema_table_CT 
ADD HostName nvarchar(50) NULL DEFAULT(HOST_NAME())

Dies gibt jedoch den Hostnamen des Servers zurück, auf dem die Abfrage ausgelöst wurde, und nicht den Hostnamen des Clients, der die Abfrage ausgelöst hat.

Gibt es einen Weg, um dieses Problem zu umgehen? Etwas, das helfen würde, den Hostnamen oder die IP-Adresse (oder eine andere Art von eindeutiger Identität) des Clients zu protokollieren. Ich möchte keine Trigger verwenden, da dies das System verlangsamt. Außerdem generiert CDC Systemtabellen, sodass ein Trigger anscheinend nicht möglich ist.

Ritesh Bhakre
quelle

Antworten:

4

Ich bin mir bei CDC nicht sicher, aber wenn der Login vorhanden ist view server state permission, können Sie DMVs verwenden, um Informationen zu erhalten.

Dies ist in Books Online hier angegeben . Ich habe die Abfrage geändert, um Spalten hinzuzufügen, die Folgendes ergeben IP address:

SELECT 
    c.session_id, c.net_transport, c.encrypt_option, c.auth_scheme,
    s.host_name, s.program_name, s.client_interface_name,
    c.local_net_address, c.client_net_address, s.login_name, s.nt_domain, 
    s.nt_user_name, s.original_login_name, c.connect_time, s.login_time 
FROM sys.dm_exec_connections AS c
JOIN sys.dm_exec_sessions AS s
    ON c.session_id = s.session_id
WHERE c.session_id = SPID;  --session ID you want to track
Shanky
quelle
4

Wenn Sie sagen, „ohne Trigger zu verwenden“, meinen Sie alle Trigger oder nur Zeile- für -Zeile - Trigger auf Tabellen?

Ich frage, weil Sie möglicherweise in der Lage sind, mit einer vernünftigen Verwendung der CONTEXT_INFO()Funktion das zu erreichen, was Sie wollen , aber Sie müssten sicherstellen, dass dies SET CONTEXT_INFOkorrekt aufgerufen wurde, bevor Ihre Operationen stattfinden.

Ein Ort, an dem dies möglich ist, kann ein Anmeldetrigger auf Serverebene sein (dh kein Trigger auf Datenbank- / Objektebene), wie folgt:

USE master
GO
CREATE TRIGGER tr_audit_login
ON ALL SERVER 
WITH EXECUTE AS 'sa'
AFTER LOGON
AS BEGIN
    BEGIN TRY

        DECLARE @eventdata XML = EVENTDATA();

        IF @eventdata IS NOT NULL BEGIN
            DECLARE @spid INT;
            DECLARE @client_host VARCHAR(64);
            SET @client_host    = @eventdata.value('(/EVENT_INSTANCE/ClientHost)[1]',   'VARCHAR(64)');
            SET @spid           = @eventdata.value('(/EVENT_INSTANCE/SPID)[1]',         'INT');

            -- pack the required data into the context data binary
            -- (spid is just an example of packing multiple data items in a single field: you would probably use @@SPID at the point of use, instead)
            DECLARE @context_data VARBINARY(128);
            SET @context_data = CONVERT(VARBINARY(4),  @spid)
                              + CONVERT(VARBINARY(64), @client_host);

            -- persist the spid and host into session-level memory
            SET CONTEXT_INFO @context_data;             
        END

    END TRY
    BEGIN CATCH
        /* do better error handling here...
         * logon trigger can lock all users out of server, so i am just swallowing everything
         */
        DECLARE @msg NVARCHAR(4000) = ERROR_MESSAGE();
        RAISERROR('%s', 10, 1, @msg) WITH LOG;
    END CATCH
END

Sie können dann der Tabelle die Standardeinschränkung hinzufügen, um den Kontext zu speichern (für die Geschwindigkeit des Einfügens):

ALTER TABLE cdc.schema_table_CT 
ADD ContextInfo varbinary(128) NULL DEFAULT(CONTEXT_INFO())

Sobald Sie das haben, können Sie diese ContextInfoSpalte mit ein bisschen Slice-and-Dice abfragen :

SELECT *
    ,spid = CONVERT(INT, SUBSTRING(ContextInfo, 1, 4))
    ,client = CONVERT(VARCHAR(64), SUBSTRING(ContextInfo, 5, 64))
FROM cdc.schema_table_CT

Technisch gesehen könnten Sie dies SUBSTRINGund das CONVERTals Teil Ihrer Standardeinschränkung tun und nur die Client-IP dort speichern, aber es kann schneller sein, den gesamten Kontext dort zu speichern (wie bei jedem INSERT), und nur die Werte in a zu extrahierenSELECT wenn du sie brauchst.

Ich könnte geneigt sein, alle meine SUBSTRINGund CONVERTAufrufe in eine einzeilige Inline-Tabellenwertfunktion zu packen, die ich CROSS APPLYbei Bedarf tun würde . Das hält die Auspacklogik an einem Ort:

CREATE FUNCTION fn_context (
    @context_info VARBINARY(128)
)
RETURNS TABLE
AS RETURN (
    SELECT
         spid = CONVERT(INT, SUBSTRING(@context_info, 1, 4))
        ,client = CONVERT(VARCHAR(64), SUBSTRING(@context_info, 5, 64))
)
GO

SELECT * 
FROM cdc.schema_table_CT s
CROSS APPLY dbo.fn_context(s.ContextInfo) c

Beachten Sie, dass dies CONTEXT_INFOnur ein 128-Byte ist VARBINARY. Wenn Sie mehr Daten benötigen, als Sie in 128 Byte einpassen können, würde ich eine Tabelle erstellen, die alle diese Daten enthält, als Zeile für diese 'Sitzung' in die Tabelle im Anmeldetrigger einfügen und CONTEXT_INFOauf den Ersatzschlüsselwert dieser Tabelle setzen

Sie sollten auch beachten, dass es für einen Benutzer mit geeigneten Berechtigungen trivial ist, diese Kontextdaten in der Tabelle "In Ruhe" zu überschreiben, da dies nur eine Standardeinschränkung ist. Dies gilt natürlich auch für alle anderen Spalten in Tabellen im Audit-Stil.

Es wäre schön, wenn es sich um eine persistierte berechnete Spalte und nicht um eine Standardspalte handeln könnte, aber die CONTEXT_INFO()Funktion ist nicht deterministisch, daher ist sie ein No-Go (Sie können möglicherweise einige FUNCTIONTricks um a herum anwendenVIEW , aber ich würde es nicht ).

Es ist auch trivial für diesen Benutzer mit ausreichendem Zugriff auf Anrufe SET CONTEXT_INFO selbst und Ihren Tag durcheinander zu bringen (z. B. mit gefälschten Werten oder speziell angefertigter gespeicherter Injektion). Behandeln Sie den Inhalt daher mit Argwohn und Sorgfalt, codieren Sie ihn vor der Anzeige und behandeln Sie Ausnahmen Gut.

Was den Hostnamen betrifft, gibt Ihnen das ClientHostElement von EVENTDATA()die IP-Adresse (oder einen <local machine>Indikator). Technisch gesehen könnten Sie CLR verwenden, um Reverse-DNS-Suchvorgänge zurück zum Hostnamen durchzuführen. Diese sind jedoch in der Regel zu langsam INSERT, sodass ich dies nicht empfehlen würde zu tun.

Wenn Sie haben einen Hostnamen haben, können Sie einen SQL - Agent - Auftrag verwenden , um in regelmäßigen Abständen eine separate Tabelle mit dem aktuellen Leases von Ihrer lokalen DHCP - Server oder DNS - Zonendatei, als Out-of-Band - Prozess bevölkert , und LEFT JOINder in zukünftige Abfragen (oder in einen Skalar einschließen FUNCTION, um einen Wert für eine Standardeinschränkung für den Zeitpunkt bereitzustellen).

Auch hier sollten Sie beachten, dass IP-Adressen und Hostnamen unzuverlässig sind (z. B. aufgrund von NAT), wenn die Anwendung eine öffentlich zugängliche Komponente enthält. Auch wenn es nicht öffentlich zugänglich ist, enthalten die meisten IP- / Hostnamen-Maps eine bestimmte zeitbasierte Komponente, die Sie möglicherweise berücksichtigen müssen.

Bevor Sie Ihren Anmeldetrigger implementieren, kann es sich lohnen, die dedizierte Administratorverbindung Ihres Servers zu aktivieren. Wenn der Anmeldeauslöser in irgendeiner Weise unterbrochen wird, kann er verhindern, dass sich alle Benutzer anmelden (einschließlich der Sysadmin-Konten):

USE master
GO
-- you may want to do this, so you have a back-out if the login trigger breaks login
EXEC sp_configure 'remote admin connections', 1 
GO
RECONFIGURE
GO

Wenn Sie gesperrt werden, kann der DAC verwendet werden, um den Anmeldetrigger zu löschen oder zu deaktivieren:

C:\> sqlcmd -S localhost -d master -A
1> DISABLE TRIGGER tr_audit_login ON ALL SERVER
2> GO
jimbobmcgee
quelle
3

Bitte werfen Sie einen Blick auf den Verbindungsfehler : Unten finden Sie den entsprechenden Ausschnitt daraus

Dieses Verhalten ist beabsichtigt. CDC bietet die folgenden Informationen zu einer Änderung: aktualisierte Spalten, Art der Operation und Transaktionsinformationen. Es wurde nicht als Prüfungslösung konzipiert. Es wurde entwickelt, um effiziente Extract Transfer and Load-Lösungen (ETL) durch inkrementelles Laden von Daten zu ermöglichen, was der Schlüssel zur Reduzierung der gesamten ETL-Zeit ist. Sein Hauptziel ist es, "was sich geändert hat" aufzudecken, nicht wer, wann ... Dafür empfehle ich die SQL Audit-Funktion.

Derzeit ist nicht geplant, CDC in eine Audit-Lösung umzuwandeln.

Jens W.
quelle