ค้นหาตัวตนของลูกค้าที่เริ่มสืบค้นใน SQL Server โดยไม่ใช้ทริกเกอร์หรือไม่


11

ขณะนี้ฉันใช้Change Data Capture (CDC)เพื่อติดตามการเปลี่ยนแปลงข้อมูลและฉันต้องการติดตามชื่อโฮสต์และที่อยู่ IP ของลูกค้าที่ส่งการสอบถามที่ทำการเปลี่ยนแปลง หากมีลูกค้าที่แตกต่างกัน 5 คนลงชื่อเข้าใช้ด้วยชื่อผู้ใช้เดียวกันคนหนึ่งเผชิญกับปัญหาในการติดตามซึ่งหนึ่งในห้านั้นทำการสืบค้น โซลูชันที่กว้างขวางอื่น ๆ ที่ฉันพบรวมถึงการแก้ไขตาราง CDC ด้วยคำสั่งต่อไปนี้:

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

อย่างไรก็ตามสิ่งนี้จะส่งคืนชื่อโฮสต์ของเซิร์ฟเวอร์ที่เปิดใช้งานการสืบค้นไม่ใช่ชื่อโฮสต์ของไคลเอนต์ที่เปิดใช้งานการสอบถาม

มีวิธีแก้ไขปัญหานี้หรือไม่? สิ่งที่จะช่วยในการบันทึกชื่อโฮสต์หรือที่อยู่ IP (หรือเอกลักษณ์เฉพาะอื่น ๆ ) ของลูกค้า ฉันไม่ต้องการใช้ทริกเกอร์เนื่องจากมันทำให้ระบบช้าลง CDC ก็สร้างตารางระบบดังนั้นจึงมีทริกเกอร์ที่ไม่สามารถทำได้

คำตอบ:


4

ฉันไม่แน่ใจเกี่ยวกับ CDC แต่ถ้ามีการเข้าสู่ระบบview server state permissionคุณสามารถใช้ DMV เพื่อรับข้อมูลบางอย่าง

นี้จะได้รับในหนังสือออนไลน์ที่นี่ ฉันเปลี่ยนแบบสอบถามเพื่อเพิ่มคอลัมน์ที่จะให้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ได้รับการโทรอย่างถูกต้องก่อนที่การดำเนินการของคุณจะเกิดขึ้น

ที่เดียวที่ต้องทำนั่นอาจเป็นทริกเกอร์การเข้าสู่ระบบในระดับเซิร์ฟเวอร์ (เช่นไม่ใช่ทริกเกอร์ฐานข้อมูล / ระดับวัตถุ) ดังนี้:

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

ในทางเทคนิคคุณสามารถทำสิ่งนั้นSUBSTRINGและCONVERTเป็นส่วนหนึ่งของข้อ จำกัด เริ่มต้นของคุณและเพียงแค่เก็บ IP ของลูกค้าที่นั่น แต่มันอาจจะเร็วกว่าที่จะเก็บบริบททั้งหมดที่นั่น (ตามที่ทำในทุก ๆINSERT) และแยกค่าในSELECTเมื่อคุณต้องการพวกเขา

ฉันอาจมีแนวโน้มที่จะห่อของฉันทั้งหมดSUBSTRINGและการCONVERTโทรในฟังก์ชั่นมูลค่าตารางแบบอินไลน์แถวเดียวซึ่งฉันจะCROSS APPLYเมื่อจำเป็น ที่ช่วยให้ตรรกะการเปิดออกในที่เดียว:

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คีย์ตัวแทนของตารางนั้น

คุณควรทราบด้วยว่าเนื่องจากเป็นเพียงข้อ จำกัด เริ่มต้นจึงเป็นเรื่องเล็กน้อยสำหรับผู้ใช้ที่มีสิทธิพิเศษที่จะเขียนทับข้อมูลบริบทนั้นในตารางที่เหลือ แน่นอนเช่นเดียวกันสำหรับคอลัมน์อื่น ๆ ทั้งหมดในตารางสไตล์ 'audit' เช่นกัน

มันจะดีถ้ามันอาจจะเป็นคอลัมน์ที่คำนวณไว้แทนที่จะเป็นค่าเริ่มต้น แต่CONTEXT_INFO()ฟังก์ชั่นนั้นไม่ได้กำหนดไว้ดังนั้นมันจึงเป็นแบบไม่ไป (คุณอาจใช้FUNCTIONกลอุบายรอบ ๆVIEWได้ แต่ฉันจะไม่ )

นอกจากนี้ยังเป็นเรื่องเล็กน้อยสำหรับผู้ใช้ที่มีการเข้าถึงที่เพียงพอเพื่อเรียกSET CONTEXT_INFOตัวเองและทำให้ยุ่งเหยิงในวันของคุณ (เช่นค่าของปลอมหรือการฉีดที่จัดทำขึ้นเป็นพิเศษ) ดังนั้นรักษาเนื้อหาด้วยความสงสัยและการดูแลรักษาเข้ารหัสก่อนแสดงและจัดการข้อยกเว้น ดี.

สำหรับชื่อโฮสต์ฉันคิดว่าClientHostองค์ประกอบของEVENTDATA()ให้ที่อยู่ IP (หรือ<local machine>ตัวบ่งชี้) ให้คุณ ในขณะที่คุณสามารถใช้ CLR ในการค้นหา reverse-DNS กลับไปที่ชื่อโฮสต์ได้ แต่สิ่งเหล่านี้มักจะช้าเกินไปสำหรับทุกคนINSERTดังนั้นฉันขอแนะนำไม่ให้ทำเช่นนั้น

หากคุณต้องมีชื่อโฮสต์คุณอาจต้องการใช้งานตัวแทน SQL เพื่อเติมข้อมูลตารางแยกต่างหากเป็นระยะโดยมีสัญญาเช่าปัจจุบันจากเซิร์ฟเวอร์ DHCP ท้องถิ่นหรือไฟล์โซน DNS ของคุณเป็นกระบวนการนอกวงและLEFT JOINใน ข้อความค้นหาในอนาคต (หรือตัดคำในเซนต์คิตส์และเนวิสFUNCTIONเพื่อให้ค่ากับข้อ จำกัด เริ่มต้นสำหรับจุดในเวลา)

อีกครั้งคุณควรทราบว่าหากแอปพลิเคชันมีองค์ประกอบสาธารณะประเภทใดที่อยู่ IP และชื่อโฮสต์จะไม่น่าเชื่อถือ (เช่นเนื่องจาก NAT) แม้ว่าจะไม่ใช่แบบสาธารณะ แต่ก็มีองค์ประกอบตามเวลาในการแม็พ IP / ชื่อโฮสต์ส่วนใหญ่ซึ่งคุณอาจจำเป็นต้องคำนึงถึง

สุดท้ายก่อนที่จะใช้ทริกเกอร์การเข้าสู่ระบบของคุณอาจเป็นการเปิดการเชื่อมต่อผู้ดูแลระบบของเซิร์ฟเวอร์ของคุณโดยเฉพาะ หากทริกเกอร์การเข้าสู่ระบบแตกในทางใด ๆ ก็สามารถป้องกันผู้ใช้ทุกคนเข้าสู่ระบบ (รวมถึงบัญชีดูแลระบบ):

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 ได้รับการออกแบบมาเพื่อแสดงข้อมูลเกี่ยวกับการเปลี่ยนแปลงดังต่อไปนี้: คอลัมน์ที่อัปเดต, ประเภทการทำงานและข้อมูลธุรกรรม ไม่ได้ออกแบบมาเพื่อแก้ปัญหาการตรวจสอบ มันถูกสร้างขึ้นเพื่อเปิดใช้งานโซลูชัน Extract Transfer and Load (ETL) ที่มีประสิทธิภาพผ่านการโหลดข้อมูลที่เพิ่มขึ้นซึ่งเป็นกุญแจสำคัญในการลดเวลา ETL โดยรวม เป้าหมายหลักของมันคือการเปิดเผย "สิ่งที่เปลี่ยนแปลง" ไม่ใช่ใครเมื่อ ... สำหรับสิ่งที่ฉันแนะนำคุณลักษณะการตรวจสอบ SQL

ณ ตอนนี้ยังไม่มีแผนที่จะเปลี่ยน CDC ในโซลูชันการตรวจสอบ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.