トリガーを使用せずにSQL Serverでクエリを実行するクライアントのIDを見つけますか?


11

現在Change Data Capture(CDC)を使用してデータの変更を追跡しています。変更を行ったクエリを送信するクライアントのホスト名とIPアドレスを追跡したいと考えています。同じユーザー名でログインしている5つの異なるクライアントがある場合、5つのうちどれがクエリを起動したかを追跡するという難題に直面します。私が見つけた他の疑わしい解決策には、次のコマンドでCDCテーブルを変更することが含まれます。

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

ただし、これは、クエリが起動されたサーバーのホスト名を返し、クエリを起動したクライアントのホスト名は返しません。

この問題を回避する方法はありますか?クライアントのホスト名またはIPアドレス(または他の種類の一意のID)をログに記録するのに役立つもの。トリガーを使用したくありません。システムが遅くなるだけでなく、CDCがシステムテーブルを生成するため、トリガーを設定することは明らかに不可能です。

回答:


4

CDCについてview server state permissionはわかりませんが、ログインできれば、DMVを使用して情報を取得できます。

これはBooks Onlineでここに与えられます。クエリを変更して、次のような列を追加しました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

4

あなたが言うとき、「トリガーを使用せずに」、あなたが意味するかいずれかのテーブル上のトリガまたは単に行ごとのトリガーを?

あなたはので、私は尋ねることを賢明に利用して欲しいものを得ることができるCONTEXT_INFO()機能がありますが、それを保証する必要があるでしょうSET CONTEXT_INFO、あなたの操作が行われる前に、正しく呼び出されました。

そのための1つの場所は、次のようにサーバーレベルのログオントリガー(つまり、データベース/オブジェクトレベルのトリガーではない)かもしれません。

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

次に、デフォルトの制約をテーブルに追加して、コンテキストを格納します(挿入速度を上げるため)。

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

それができたらContextInfo、少しスライスアンドダイスでその列をクエリできます。

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

技術的には、それをデフォルトの制約の一部として実行しSUBSTRINGCONVERTそこにクライアントIPを格納するだけで済みますが、そこにコンテキスト全体を格納する方が速く(すべてので実行されるためINSERT)、値を抽出するのはSELECTあなたがそれらを必要とするとき。

私はすべてのSUBSTRINGCONVERT呼び出しを単一行のインラインテーブル値関数でラップする傾向があるかもしれませんCROSS APPLY。これにより、展開ロジックが1か所に保持されます。

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

CONTEXT_INFOは128バイトにすぎないことに注意してくださいVARBINARY。128バイトに収まらないデータが必要な場合は、すべてのデータを保持するテーブルを作成し、その「セッション」の行としてログオントリガーのテーブルに挿入し、そのテーブルCONTEXT_INFOのサロゲートキー値に設定します

また、これはデフォルトの制約にすぎないため、適切な特権のあるユーザーが保存テーブルのコンテキストデータを上書きするのは簡単です。もちろん、同じことが「監査」スタイルのテーブルの他のすべての列にも当てはまります。

既定ではなく永続的な計算列であるとよいのですが、CONTEXT_INFO()関数は非決定的であるため、FUNCTIONノーゴーです(の周りにいくつかのトリックを使用できるかもしれませんがVIEW、私はしません)。

また、SET CONTEXT_INFO自分を呼び出すために十分なアクセス権を持ち、1日を混乱させる(たとえば、偽の値、または特別に細工された保存された注射で)そのユーザーにとっては些細なことです。上手。

ホスト名に関しては、のClientHost要素がEVENTDATA()IPアドレス(または<local machine>インジケータ)を与えると思います。あなたが技術的に戻って、ホスト名への逆DNSルックアップを行うためにCLRを使用することができますが、これらはすべてのために行うにはあまりにも遅くなる傾向がありINSERT、私が推薦するので、ないそうします。

あなたがいる場合持っているホスト名を持っている、あなたは定期的にアウトオブバンドプロセスとして、そして、ローカルのDHCPサーバーやDNSゾーンファイルから現在のリースを別のテーブルを移入するためにSQLエージェントジョブを使用する場合がありLEFT JOIN、その中に今後のクエリ(または、スカラーFUNCTIONにラップして、特定の時点のデフォルトの制約に値を提供します)。

繰り返しになりますが、アプリケーションに何らかの種類の公開コンポーネントがある場合、IPアドレスとホスト名は(NATなどにより)信頼できないことに注意してください。一般に公開されていなくても、ほとんどのIP /ホスト名マップには特定の時間ベースのコンポーネントがあるため、これを考慮に入れる必要があります。

最後に、ログイントリガーを実装する前に、サーバーの専用管理接続をオンにすることをお勧めします。ログイントリガーが何らかの方法で壊れると、すべてのユーザーがログインできなくなります(sysadminアカウントを含む)。

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

ロックアウトされた場合、DACを使用して、ログイントリガーを削除または無効にすることができます。

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

3

接続バグを見てください:以下は、関連するスニペットです

この動作は仕様です。CDCは、変更に関する次の情報を公開するように設計されています:更新された列、操作のタイプ、およびトランザクション情報。監査ソリューションとして設計されていません。全体的なETL時間を短縮するための鍵となる増分データロードを通じて、効率的なExtract Transfer and Loadソリューション(ETL)を実現するために作成されました。その主な目的は、誰が、いつではなく、「何が変わったか」を明らかにすることです...そのために、SQL監査機能をお勧めします。

現在のところ、CDCを監査ソリューションに変換する計画はありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.