Знайти особу клієнта, що запускає запит у SQL Server, не використовуючи тригери?


11

Наразі я використовую програму Capture Data Change (CDC) для відстеження змін даних, і я хочу відстежувати ім'я хоста та IP-адресу клієнта, який подає запит, який вніс зміни. Якщо є 5 різних клієнтів, які ввійшли через те саме ім’я користувача, кожен стикається з загальною проблемою відстеження, хто з 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сурогатне значення ключової таблиці цієї таблиці

Слід також зазначити, що, оскільки це лише обмеження за замовчуванням, тривіально для користувача, що має належні права, може перезаписати ці контекстні дані в таблицю спокою. Звичайно, те ж саме стосується і всіх інших стовпців у таблицях у стилі "аудит".

Було б добре, якби це міг бути стійкий обчислюваний стовпець, а не за замовчуванням, але CONTEXT_INFO()функція недетермінована, тому вона не працює (ви, можливо, зможете використовувати деякі FUNCTIONхитрощі навколо VIEW, але я б не ).

Це також банально для цього користувача, який має достатній доступ, щоб зателефонувати SET CONTEXT_INFOсобі та зіпсувати свій день (наприклад, з підробленими значеннями або спеціально складеним збереженим введенням), тому ставіться до вмісту з підозрою та обережністю, кодуйте його до показу та обробляйте винятки Ну.

Що стосується імені хоста, я думаю, що ClientHostелемент EVENTDATA()надає вам IP-адресу (або <local machine>індикатор). Хоча ви технічно можете використовувати CLR для зворотного пошуку DNS назад до імені хоста, вони, як правило, занадто повільно робити для кожного INSERT, тому я рекомендую цього не робити.

Якщо у вас є ім'я хоста, ви можете використовувати завдання SQL Agent, щоб періодично заповнювати окрему таблицю поточними орендами з вашого локального сервера DHCP або DNS-зони, як поза межами діапазону, і 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

Якщо ви заблоковані, ЦАП може бути використаний для скидання або відключення тригера входу:

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

3

Погляньте на помилку підключення . Нижче наведено відповідний фрагмент з неї

Така поведінка за задумом. CDC призначений для викриття такої інформації про зміни: оновлені стовпці, тип операції та інформація про транзакції. Він не розглядався як рішення аудиту. Він був створений для забезпечення ефективних рішень для передачі та завантаження екстрактів (ETL) за рахунок додаткового навантаження даних, що є ключовим для зменшення загального часу ETL. Основна його мета - виявити "що змінилося", а не хто, коли ... Для цього я рекомендую функцію аудиту SQL.

На сьогоднішній день не планується перетворення CDC у рішення аудиту.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.