Database
 sql >> база данни >  >> RDS >> Database

Вътрешните елементи на С КРИПЦИЯ

За администратора на SQL Server е доста лесно да възстанови текста на съхранените процедури, изгледи, функции и тригери, защитени с помощта на WITH ENCRYPTION . За това са написани много статии и са налични няколко търговски инструмента. Основното описание на общия метод е да:

  1. Получете криптирания формуляр (A), като използвате специалната администраторска връзка.
  2. Започнете транзакция.
  3. Заменете дефиницията на обекта с известен текст (B) с поне същата дължина като оригинала.
  4. Получете криптираната форма за известния текст (C).
  5. Превърнете транзакцията назад, за да оставите целевия обект в първоначалното му състояние.
  6. Получете некриптирания оригинал, като приложите изключителен или към всеки знак:A XOR (B XOR C)

Всичко това е доста просто, но изглежда малко като магия:не обяснява много за как и защо работи . Тази статия обхваща този аспект за онези от вас, които намират подобни подробности за интересни, и предоставя алтернативен метод за декриптиране, който е по-илюстративен за процеса.

Шифърът на потока

Основният алгоритъм за криптиране, който SQL Server използва за криптиране на модул е потоковият шифър RC4™. Очертанието на процеса на криптиране е:

  1. Инициализирайте шифъра RC4 с криптографски ключ.
  2. Генерирайте псевдослучаен поток от байтове.
  3. Комбинирайте обикновения текст на модула с потока от байтове, като използвате изключителни или.

Можем да видим, че този процес се случва с помощта на дебъгер и публични символи. Например, следата на стека по-долу показва SQL Server, инициализиращ ключа RC4, докато се подготвя да криптира текста на модула:

Следващият показва, че SQL Server криптира текста с помощта на псевдослучаен байтов поток RC4:

Подобно на повечето поточни шифри, процесът на декриптиране е същият като криптирането, като се използва фактът, че изключителният или е обратим (A XOR B XOR B = A ).

Използването на поточен шифър е причината изключително-или се използва в метода, описан в началото на статията. Няма нищо опасно по своята същност в използването на изключителна или, при условие че се използва защитен метод за криптиране, ключът за инициализация се пази в тайна и ключът не се използва повторно.

RC4 не е особено силен, но това не е основният проблем тук. Въпреки това си струва да се отбележи, че криптирането с помощта на RC4 постепенно се премахва от SQL Server и е отхвърлено (или деактивирано, в зависимост от версията и нивото на съвместимост на базата данни) за потребителски операции като създаване на симетричен ключ.

Ключът за инициализация RC4

SQL Server използва три части от информация, за да генерира ключа, използван за инициализиране на шифъра на потока RC4:

  1. GUID на семейството на базата данни.

    Това може да се получи най-лесно чрез заявка за sys.database_recovery_status . Вижда се и в недокументирани команди като DBCC DBINFO и DBCC DBTABLE .

  2. Идентификаторът на обекта на целевия модул.

    Това е просто познатият идентификатор на обекта. Имайте предвид, че не всички модули, които позволяват криптиране, са с обхват на схема. Ще трябва да използвате изгледи на метаданни (sys.triggers или sys.server_triggers ), за да получите идентификатора на обекта за DDL и тригери с обхват на сървъра, а не за sys.objects или OBJECT_ID , тъй като те работят само с обекти с обхват на схема.

  3. Идентификаторът на подобекта на целевия модул.

    Това е номерът на процедурата за номерирани съхранени процедури. Това е 1 за неномерирана съхранена процедура и нула във всички останали случаи.

Използвайки отново инструмента за отстраняване на грешки, можем да видим, че семейният GUID се извлича по време на инициализацията на ключа:

GUID на семейството на базата данни е въведен uniqueidentifier , идентификаторът на обекта е цяло число , а идентификаторът на подобект е smallint .

Всяка част от ключзата да бъдат преобразувани в определен двоичен формат. За GUID на семейството на базата данни, преобразуване на уникалния идентификатор въведете двоичен(16) произвежда правилното двоично представяне. Двата идентификатора трябва да бъдат преобразувани в двоичен в представяне с малък байт (първо най-малко значим байт).

Забележка: Бъдете много внимателни да не предоставите случайно GUID като низ! Трябва да бъде въведен uniqueidentifier .

Кодовият фрагмент по-долу показва правилни операции за преобразуване за някои примерни стойности:

DECLARE 
    @family_guid binary(16) = CONVERT(binary(16), {guid 'B1FC892E-5824-4FD3-AC48-FBCD91D57763'}),
    @objid binary(4) = CONVERT(binary(4), REVERSE(CONVERT(binary(4), 800266156))),
    @subobjid binary(2) = CONVERT(binary(2), REVERSE(CONVERT(binary(2), 0)));

Последната стъпка за генериране на ключа за инициализация RC4 е да се свържат трите двоични стойности по-горе в един двоичен файл(22) и да се изчисли SHA-1 хеш на резултата:

DECLARE 
    @RC4key binary(20) = HASHBYTES('SHA1', @family_guid + @objid + @subobjid);

За примерните данни, дадени по-горе, крайният ключ за инициализация е:

0x6C914908E041A08DD8766A0CFEDC113585D69AF8

Приносът на идентификатора на обекта на целевия модул и идентификатора на подобекта към SHA-1 хеша е трудно да се види в една екранна снимка на програмата за отстраняване на грешки, но заинтересованият читател може да се обърне към разглобяването на част от initspkey по-долу:

call    sqllang!A_SHAInit
lea     rdx,[rsp+40h]
lea     rcx,[rsp+50h]
mov     r8d,10h
call    sqllang!A_SHAUpdate
lea     rdx,[rsp+24h]
lea     rcx,[rsp+50h]
mov     r8d,4
call    sqllang!A_SHAUpdate
lea     rdx,[rsp+20h]
lea     rcx,[rsp+50h]
mov     r8d,2
call    sqllang!A_SHAUpdate
lea     rdx,[rsp+0D0h]
lea     rcx,[rsp+50h]
call    sqllang!A_SHAFinal
lea     r8,[rsp+0D0h]
mov     edx,14h
mov     rcx,rbx
call    sqllang!rc4_key (00007fff`89672090)

SHAInit и SHAUupdate повиквания добавят компоненти към SHA хеша, който в крайна сметка се изчислява чрез извикване на SHAFinal .

SHAInit повикването предоставя 10h байта (16 десетични), съхранени в [rsp+40h], което е семейният GUID . Първата SHAUактуализация повикването добавя 4 байта (както е посочено в регистъра r8d), съхранени в [rsp+24h], което е обектът ДОКУМЕНТ ЗА САМОЛИЧНОСТ. Втората SHAUактуализация повикването добавя 2 байта, съхранени в [rsp+20h], което е subobjid .

Последните инструкции предават изчисления SHA-1 хеш към рутинната процедура за инициализация на RC4 ключ rc4_key . Дължината на хеша се съхранява в регистър edx:14h (20 десетични) байта, което е дефинираната дължина на хеша за SHA и SHA-1 (160 бита).

Реализацията на RC4

Основният RC4 алгоритъм е добре познат и сравнително прост. Би било по-добре да се реализира на .Net език от съображения за ефективност и производителност, но по-долу има реализация на T-SQL.

Тези две T-SQL функции имплементират алгоритъма за планиране на ключове RC4 и генератора на псевдослучайни числа и първоначално са написани от MVP на SQL Server Питър Ларсон. Направих някои незначителни модификации, за да подобря малко производителността и да позволя двоични файлове с дължина на LOB да бъдат кодирани и декодирани. Тази част от процеса може да бъде заменена от всяка стандартна реализация на RC4.

/*
** RC4 functions
** Based on http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=76258
** by Peter Larsson (SwePeso)
*/
IF OBJECT_ID(N'dbo.fnEncDecRc4', N'FN') IS NOT NULL
    DROP FUNCTION dbo.fnEncDecRc4;
GO
IF OBJECT_ID(N'dbo.fnInitRc4', N'TF') IS NOT NULL
    DROP FUNCTION dbo.fnInitRc4;
GO
CREATE FUNCTION dbo.fnInitRc4
    (@Pwd varbinary(256))
RETURNS @Box table
    (
        i tinyint PRIMARY KEY, 
        v tinyint NOT NULL
    )
WITH SCHEMABINDING
AS
BEGIN
    DECLARE @Key table
    (
        i tinyint PRIMARY KEY,
        v tinyint NOT NULL
    );
 
    DECLARE
        @Index smallint = 0,
        @PwdLen tinyint = DATALENGTH(@Pwd);
 
    WHILE @Index <= 255
    BEGIN
        INSERT @Key
            (i, v)
        VALUES
            (@Index, CONVERT(tinyint, SUBSTRING(@Pwd, @Index % @PwdLen + 1, 1)));
 
        INSERT @Box (i, v)
        VALUES (@Index, @Index);
 
        SET @Index += 1;
    END;
 
    DECLARE
        @t tinyint = NULL,
        @b smallint = 0;
 
    SET @Index = 0;
 
    WHILE @Index <= 255
    BEGIN
        SELECT @b = (@b + b.v + k.v) % 256
        FROM @Box AS b
        JOIN @Key AS k
            ON k.i = b.i
        WHERE b.i = @Index;
 
        SELECT @t = b.v
        FROM @Box AS b
        WHERE b.i = @Index;
 
        UPDATE b1
        SET b1.v = (SELECT b2.v FROM @Box AS b2 WHERE b2.i = @b)
        FROM @Box AS b1
        WHERE b1.i = @Index;
 
        UPDATE @Box
        SET v = @t
        WHERE i = @b;
 
        SET @Index += 1;
    END;
 
    RETURN;
END;
GO
CREATE FUNCTION dbo.fnEncDecRc4
(
    @Pwd varbinary(256),
    @Text varbinary(MAX)
)
RETURNS varbinary(MAX)
WITH 
    SCHEMABINDING, 
    RETURNS NULL ON NULL INPUT
AS
BEGIN
    DECLARE @Box AS table 
    (
        i tinyint PRIMARY KEY, 
        v tinyint NOT NULL
    );
 
    INSERT @Box
        (i, v)
    SELECT
        FIR.i, FIR.v
    FROM dbo.fnInitRc4(@Pwd) AS FIR;
 
    DECLARE
        @Index integer = 1,
        @i smallint = 0,
        @j smallint = 0,
        @t tinyint = NULL,
        @k smallint = NULL,
        @CipherBy tinyint = NULL,
        @Cipher varbinary(MAX) = 0x;
 
    WHILE @Index <= DATALENGTH(@Text)
    BEGIN
        SET @i = (@i + 1) % 256;
 
        SELECT
            @j = (@j + b.v) % 256,
            @t = b.v
        FROM @Box AS b
        WHERE b.i = @i;
 
        UPDATE b
        SET b.v = (SELECT w.v FROM @Box AS w WHERE w.i = @j)
        FROM @Box AS b
        WHERE b.i = @i;
 
        UPDATE @Box
        SET v = @t
        WHERE i = @j;
 
        SELECT @k = b.v
        FROM @Box AS b
        WHERE b.i = @i;
 
        SELECT @k = (@k + b.v) % 256
        FROM @Box AS b
        WHERE b.i = @j;
 
        SELECT @k = b.v
        FROM @Box AS b
        WHERE b.i = @k;
 
        SELECT
            @CipherBy = CONVERT(tinyint, SUBSTRING(@Text, @Index, 1)) ^ @k,
            @Cipher = @Cipher + CONVERT(binary(1), @CipherBy);
 
        SET @Index += 1;
    END;
 
    RETURN @Cipher;
END;
GO

Шифрованият текст на модула

Най-лесният начин за администратор на SQL Server да получи това е да прочете varbinary(max) стойност, съхранена в imageval колона sys.sysobjvalues , която е достъпна само чрез специалната администраторска връзка (DAC).

Това е същата идея като рутинния метод, описан във въведението, въпреки че добавяме филтър към valclass =1. Тази вътрешна таблица също е удобно място за получаване на subjid . В противен случай ще трябва да проверим sys.numbered_procedures когато целевият обект е процедура, използвайте 1 за неномерирана процедура или нула за нещо друго, както е описано по-горе.

Възможно е даизбегнете използването на DACа като прочетете imageval от sys.sysobjvalues директно, използвайки множество DBCC PAGE обаждания. Това включва малко повече работа за намиране на страниците от метаданни, следвайте imageval LOB верига и прочетете целевите двоични данни от всяка страница. Последната стъпка е много по-лесна за изпълнение на език за програмиране, различен от T-SQL. Обърнете внимание, че DBCC PAGE ще работи, въпреки че базовият обект обикновено не се чете от връзка, която не е DAC. Ако страницата не е в паметта, тя ще бъде прочетена от постоянното хранилище както обикновено.

Допълнителните усилия за избягване на изискването за DAC се изплащат, като позволяват на множество потребители да използват процеса на декриптиране едновременно. Ще използвам подхода на DAC в тази статия от съображения за простота и пространство.

Работен пример

Следният код създава тестова криптирана скаларна функция:

CREATE FUNCTION dbo.FS()
RETURNS varchar(255)
WITH ENCRYPTION, SCHEMABINDING AS
BEGIN
    RETURN 
    (
        SELECT 'My code is so awesome is needs to be encrypted!'
    );
END;

Пълната реализация на декриптиране е по-долу. Единственият параметър, който трябва да се промени, за да работи за други обекти, е първоначалната стойност на @objectid зададен в първия DECLARE изявление.

-- *** DAC connection required! ***
-- Make sure the target database is the context
USE Sandpit;
 
DECLARE
    -- Note: OBJECT_ID only works for schema-scoped objects
    @objectid integer = OBJECT_ID(N'dbo.FS', N'FN'),
    @family_guid binary(16),
    @objid binary(4),
    @subobjid binary(2),
    @imageval varbinary(MAX),
    @RC4key binary(20);
 
-- Find the database family GUID
SELECT @family_guid = CONVERT(binary(16), DRS.family_guid)
FROM sys.database_recovery_status AS DRS
WHERE DRS.database_id = DB_ID();
 
-- Convert object ID to little-endian binary(4)
SET @objid = CONVERT(binary(4), REVERSE(CONVERT(binary(4), @objectid)));
 
SELECT
    -- Read the encrypted value
    @imageval = SOV.imageval,
    -- Get the subobjid and convert to little-endian binary
    @subobjid = CONVERT(binary(2), REVERSE(CONVERT(binary(2), SOV.subobjid)))
FROM sys.sysobjvalues AS SOV
WHERE 
    SOV.[objid] = @objectid
    AND SOV.valclass = 1;
 
-- Compute the RC4 initialization key
SET @RC4key = HASHBYTES('SHA1', @family_guid + @objid + @subobjid);
 
-- Apply the standard RC4 algorithm and
-- convert the result back to nvarchar
PRINT CONVERT
    (
        nvarchar(MAX),
        dbo.fnEncDecRc4
        (
            @RC4key,
            @imageval
        )
    );

Обърнете внимание на окончателното преобразуване в nvarchar тъй като текстът на модула се въвежда като nvarchar(max) .

Резултатът е:

Заключение

Причините, поради които методът, описан във въведението, работи са:

  • SQL сървърът използва шифъра на потока RC4 за обратимо изключителен или изходен текст.
  • Ключът RC4 зависи само от ръководството на семейството на базата данни, идентификатора на обекта и subobjid.
  • Временната подмяна на текста на модула означава, че е генериран същият (SHA-1 хеширан) ключ RC4.
  • Със същия ключ се генерира същият RC4 поток, което позволява ексклузивно или декриптиране.

Потребителите, които нямат достъп до системни таблици, файлове на база данни или друг достъп на ниво администратор, не могат да извличат криптиран текст на модула. Тъй като самият SQL Server трябва да може да декриптира модула, няма начин да се попречи на потребители с подходящи права да правят същото.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Как да активирате общи регистрационни файлове и регистрационни файлове за грешки в AWS RDS

  2. Подходи за сигурност в моделирането на данни. част 3

  3. Как да моделираме за лесна поддръжка на база данни

  4. Свързване на SAS JMP към Salesforce.com

  5. Как да получите годината от дата в T-SQL