Temukan identitas klien yang menembakkan kueri di SQL Server tanpa menggunakan pemicu?

11

Saat ini saya menggunakan Change Data Capture (CDC) untuk melacak perubahan data, dan saya ingin melacak nama host dan alamat IP klien yang mengirimkan pertanyaan yang membuat perubahan. Jika ada 5 klien yang berbeda yang masuk melalui nama pengguna yang sama, seseorang menghadapi teka-teki pelacakan mana dari 5 klien yang mengeluarkan kueri. Solusi bermoral lain yang saya temukan termasuk mengubah tabel CDC dengan perintah berikut:

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

Namun, ini mengembalikan nama host dari server di mana permintaan itu dipecat, dan bukan nama host dari klien yang menembakkan kueri.

Apakah ada cara untuk mengatasi masalah ini? Sesuatu yang akan membantu untuk mencatat nama host atau alamat IP (atau sejenis identitas unik lainnya) dari klien. Saya tidak ingin menggunakan pemicu, karena memperlambat sistem, juga CDC menghasilkan tabel sistem, jadi memiliki pemicu pada yang tampaknya tidak mungkin.

Ritesh Bhakre
sumber

Jawaban:

4

Saya tidak yakin tentang CDC, tetapi jika login sudah, view server state permissionAnda dapat menggunakan DMV untuk mendapatkan informasi.

Ini diberikan dalam Buku Daring di sini . Saya mengubah kueri untuk menambahkan kolom yang akan memberi Anda 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
sumber
4

Ketika Anda mengatakan, "tanpa menggunakan pemicu", maksud Anda setiap pemicu atau hanya baris-demi-baris memicu pada tabel?

Saya bertanya karena Anda mungkin bisa mendapatkan apa yang Anda inginkan dengan penggunaan CONTEXT_INFO()fungsi yang bijaksana , tetapi Anda perlu memastikan bahwa SET CONTEXT_INFOitu dipanggil dengan benar sebelum operasi Anda berlangsung.

Satu tempat untuk melakukan itu mungkin pemicu masuk tingkat server (yaitu bukan pemicu tingkat basis data / objek), seperti:

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

Anda kemudian dapat menambahkan batasan default ke tabel Anda, untuk menyimpan konteks (untuk kecepatan memasukkan):

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

Setelah memilikinya, Anda dapat menanyakan ContextInfokolom itu dengan sedikit irisan:

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

Secara teknis, Anda bisa melakukan itu SUBSTRINGdan CONVERThal - hal sebagai bagian dari batasan default Anda, dan hanya menyimpan IP klien di sana, tetapi mungkin lebih cepat untuk menyimpan seluruh konteks di sana (seperti yang dilakukan pada setiap INSERT), dan hanya mengekstrak nilai dalam SELECTketika Anda membutuhkannya.

Saya mungkin cenderung untuk membungkus semua panggilan saya SUBSTRINGdan CONVERTdalam fungsi baris-tabel inline bernilai tunggal, yang saya akan CROSS APPLYbila diperlukan. Itu menjaga logika pembongkaran di satu tempat:

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

Perhatikan bahwa CONTEXT_INFOhanya 128 byte VARBINARY. Jika Anda membutuhkan lebih banyak data daripada yang dapat Anda muat dalam 128 byte, saya akan membuat tabel untuk menampung semua data itu, masukkan sebagai baris untuk 'sesi' itu ke dalam tabel di pemicu masuk dan setel CONTEXT_INFOke nilai kunci pengganti dari tabel itu.

Anda juga harus mencatat bahwa, karena ini hanya kendala standar, sepele bagi pengguna yang memiliki hak istimewa untuk menimpa data konteks itu di tabel at-rest. Tentu saja, hal yang sama juga berlaku untuk semua kolom lainnya dalam tabel gaya audit.

Akan lebih baik jika itu bisa menjadi kolom yang dihitung terus-menerus, daripada default, tetapi CONTEXT_INFO()fungsinya adalah non-deterministik, jadi ini adalah no-go (Anda mungkin dapat menggunakan beberapa FUNCTIONtipu daya di sekitar a VIEW, tapi saya tidak akan melakukannya. ).

Hal ini juga sepele bagi pengguna dengan akses yang cukup untuk menyebut SET CONTEXT_INFOdiri mereka sendiri dan mengacaukan hari Anda (misalnya dengan nilai-nilai palsu, atau injeksi tersimpan yang dibuat khusus), jadi perlakukan konten dengan kecurigaan dan kehati-hatian, disandikan sebelum ditampilkan, dan tangani pengecualian. baik.

Adapun nama host, saya pikir ClientHostelemen EVENTDATA()memberi Anda alamat IP (atau <local machine>indikator). Meskipun Anda secara teknis dapat menggunakan CLR untuk melakukan pencarian reverse-DNS kembali ke nama host, ini cenderung terlalu lambat untuk dilakukan untuk setiap INSERT, jadi saya akan merekomendasikan untuk tidak melakukannya.

Jika Anda harus memiliki nama host, Anda mungkin ingin menggunakan pekerjaan SQL Agent untuk secara berkala mengisi tabel terpisah dengan sewa saat ini dari server DHCP lokal atau file zona DNS, sebagai proses out-of-band, dan LEFT JOINuntuk itu dalam kueri mendatang (atau bungkus skalar FUNCTIONuntuk memberikan nilai pada batasan default, untuk point-in-time).

Sekali lagi, Anda harus mencatat bahwa, jika aplikasi memiliki komponen yang menghadap publik, alamat IP dan nama host tidak dapat diandalkan (misalnya karena NAT). Bahkan jika itu tidak menghadap ke publik, ada komponen berbasis waktu tertentu untuk sebagian besar peta IP / hostname, yang mungkin perlu Anda perhitungkan.

Terakhir, sebelum mengimplementasikan pemicu masuk Anda, mungkin perlu menyalakan koneksi admin khusus server Anda. Jika pemicu masuk terputus dengan cara apa pun, itu dapat mencegah semua pengguna masuk (termasuk akun 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

Jika Anda terkunci, DAC dapat digunakan untuk menjatuhkan atau menonaktifkan pemicu masuk:

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

Silakan lihat bug terhubung : Di bawah ini adalah cuplikan yang relevan dari itu

Perilaku ini adalah dengan desain. CDC dirancang untuk mengekspos informasi berikut tentang perubahan: kolom yang diperbarui, jenis operasi dan informasi transaksi. Itu belum dirancang sebagai solusi audit. Itu telah dibuat untuk memungkinkan solusi Extract Transfer dan Load (ETL) yang efisien melalui penambahan data yang merupakan kunci untuk mengurangi waktu ETL keseluruhan. Tujuan utamanya adalah mengekspos "apa yang telah berubah" bukan siapa, kapan ... Untuk itu saya merekomendasikan fitur SQL Audit.

Sampai sekarang tidak ada rencana untuk mengubah CDC dalam solusi Audit.

Jens W.
sumber