Database
 sql >> Baza danych >  >> RDS >> Database

Wewnętrzne elementy Z SZYFROWANIEM

Administrator programu SQL Server może dość łatwo odzyskać tekst procedur składowanych, widoków, funkcji i wyzwalaczy chronionych za pomocą WITH ENCRYPTION . Napisano na ten temat wiele artykułów i dostępnych jest kilka narzędzi komercyjnych. Podstawowy zarys popularnej metody to:

  1. Uzyskaj zaszyfrowany formularz (A) za pomocą dedykowanego połączenia administratora.
  2. Rozpocznij transakcję.
  3. Zastąp definicję obiektu znanym tekstem (B) o co najmniej tej samej długości co oryginał.
  4. Uzyskaj zaszyfrowaną formę znanego tekstu (C).
  5. Wycofaj transakcję, aby pozostawić obiekt docelowy w stanie początkowym.
  6. Uzyskaj niezaszyfrowany oryginał, stosując znak wyłączności lub do każdego znaku:A XOR (B XOR C)

To wszystko jest całkiem proste, ale wydaje się trochę magiczne:nie wyjaśnia zbyt wiele o tym, jak i dlaczego to działa . Ten artykuł obejmuje ten aspekt dla tych z Was, dla których tego rodzaju szczegóły są interesujące, i zapewnia alternatywną metodę odszyfrowywania, która lepiej ilustruje proces.

Szyfr strumienia

Bazowy algorytm szyfrowania, którego SQL Server używa do szyfrowania modułów jest szyfrem strumieniowym RC4™. Zarys procesu szyfrowania to:

  1. Zainicjuj szyfr RC4 za pomocą klucza kryptograficznego.
  2. Generuj pseudolosowy strumień bajtów.
  3. Połącz zwykły tekst modułu ze strumieniem bajtów za pomocą exclusive-lub.

Możemy zaobserwować ten proces za pomocą debuggera i symboli publicznych. Na przykład poniższy zrzut stosu pokazuje, że SQL Server inicjuje klucz RC4 podczas przygotowywania do zaszyfrowania tekstu modułu:

Ten następny pokazuje SQL Server szyfrujący tekst przy użyciu pseudolosowego strumienia bajtów RC4:

Podobnie jak większość szyfrów strumieniowych, proces odszyfrowywania jest taki sam jak szyfrowanie, wykorzystując fakt, że exclusive-or jest odwracalny (A XOR B XOR B = A ).

Użycie szyfru strumieniowego jest powodem exclusive-lub jest stosowany w metodzie opisanej na początku artykułu. Nie ma nic z natury niebezpiecznego w korzystaniu z wyłączności lub, pod warunkiem, że używana jest bezpieczna metoda szyfrowania, klucz inicjujący jest utrzymywany w tajemnicy i nie jest ponownie używany.

RC4 nie jest szczególnie mocny, ale to nie jest tutaj główny problem. To powiedziawszy, warto zauważyć, że szyfrowanie przy użyciu RC4 jest stopniowo usuwane z SQL Server i jest przestarzałe (lub wyłączone, w zależności od wersji i poziomu zgodności bazy danych) dla operacji użytkownika, takich jak tworzenie klucza symetrycznego.

Klucz inicjujący RC4

SQL Server wykorzystuje trzy informacje do wygenerowania klucza użytego do zainicjowania szyfru strumieniowego RC4:

  1. Identyfikator GUID rodziny baz danych.

    Najłatwiej to uzyskać, wysyłając zapytanie sys.database_recovery_status . Jest również widoczny w nieudokumentowanych poleceniach, takich jak DBCC DBINFO i DBCC DBTABLE .

  2. Identyfikator obiektu modułu docelowego.

    To tylko znany identyfikator obiektu. Należy zauważyć, że nie wszystkie moduły umożliwiające szyfrowanie są objęte zakresem schematu. Będziesz musiał użyć widoków metadanych (sys.triggers lub sys.server_triggers ), aby uzyskać identyfikator obiektu dla wyzwalaczy DDL i o zasięgu serwera, a nie sys.objects lub OBJECT_ID , ponieważ działają one tylko z obiektami o zakresie schematu.

  3. Identyfikator podobiektu modułu docelowego.

    Jest to numer procedury dla numerowanych procedur składowanych. Jest to 1 dla nienumerowanej procedury składowanej i zero we wszystkich innych przypadkach.

Używając ponownie debuggera, widzimy, że identyfikator GUID rodziny jest pobierany podczas inicjalizacji klucza:

Identyfikator GUID rodziny baz danych jest wpisany uniqueidentifier , identyfikator obiektu to liczba całkowita , a identyfikator podobiektu jest mały .

Każda część klucza musi być przekonwertowane na określony format binarny. W przypadku identyfikatora GUID rodziny baz danych, konwertowanie unikalnego identyfikatora wpisz do binary(16) tworzy poprawną reprezentację binarną. Dwa identyfikatory muszą zostać przekonwertowane na binarne w reprezentacji little-endian (najpierw najmniej znaczący bajt).

Uwaga: Uważaj, aby przypadkowo nie podać identyfikatora GUID jako ciągu! Musi być wpisany uniqueidentifier .

Poniższy fragment kodu pokazuje prawidłowe operacje konwersji dla niektórych przykładowych wartości:

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)));

Ostatnim krokiem do wygenerowania klucza inicjalizacji RC4 jest połączenie trzech powyższych wartości binarnych w jeden binarny(22) i obliczenie skrótu SHA-1 wyniku:

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

Dla przykładowych danych podanych powyżej ostatecznym kluczem inicjującym jest:

0x6C914908E041A08DD8766A0CFEDC113585D69AF8

Wkład identyfikatora obiektu i podobiektu modułu docelowego do skrótu SHA-1 jest trudny do zauważenia na pojedynczym zrzucie ekranu debugera, ale zainteresowany czytelnik może odnieść się do demontażu części initspkey poniżej:

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 i SHAUpdate wywołania dodają komponenty do skrótu SHA, który jest ostatecznie obliczany przez wywołanie SHAFinal .

SHAInit wywołanie dostarcza 10h bajtów (16 dziesiętnych) przechowywanych w [rsp+40h], który jest rodzinnym identyfikatorem GUID . Pierwsza SHAUpdate wywołanie dodaje 4 bajty (jak wskazano w rejestrze r8d), przechowywane w [rsp+24h], który jest obiektem ID. Druga SHAUpdate wywołanie dodaje 2 bajty, przechowywane w [rsp+20h], który jest subobjidem .

Ostateczne instrukcje przekazują obliczony skrót SHA-1 do procedury inicjalizacji klucza RC4 rc4_key . Długość skrótu jest przechowywana w rejestrze edx:14h (20 dziesiętnie) bajtów, co jest zdefiniowaną długością skrótu dla SHA i SHA-1 (160 bitów).

Wdrożenie RC4

Podstawowy algorytm RC4 jest dobrze znany i stosunkowo prosty. Byłoby lepiej zaimplementowane w języku .Net ze względu na wydajność i wydajność, ale poniżej znajduje się implementacja T-SQL.

Te dwie funkcje T-SQL implementują algorytm planowania kluczy RC4 i generator liczb pseudolosowych i zostały pierwotnie napisane przez Petera Larssona, MVP SQL Server. Wprowadziłem kilka drobnych modyfikacji, aby nieco poprawić wydajność i umożliwić kodowanie i dekodowanie plików binarnych o długości LOB. Ta część procesu może zostać zastąpiona dowolną standardową implementacją 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

Zaszyfrowany tekst modułu

Najłatwiejszym sposobem uzyskania tego przez administratora SQL Server jest przeczytanie varbinary(max) wartość przechowywana w imageval kolumna sys.sysobjvalues , który jest dostępny tylko przez dedykowane połączenie administratora (DAC).

Jest to ten sam pomysł, co rutynowa metoda opisana we wstępie, chociaż dodajemy filtr valclass =1. Ta wewnętrzna tabela jest również wygodnym miejscem do pobrania subobjid . W przeciwnym razie musielibyśmy sprawdzić sys.numbered_procedures gdy obiekt docelowy jest procedurą, użyj 1 dla procedury nienumerowanej lub zero dla czegokolwiek innego, jak opisano wcześniej.

Można unikać używania przetwornika cyfrowo-analogowego czytając imageval z sys.sysobjvalues bezpośrednio, używając wielu DBCC PAGE wzywa. Wymaga to trochę więcej pracy, aby zlokalizować strony na podstawie metadanych, postępuj zgodnie z imageval łańcucha LOB i odczytuj docelowe dane binarne z każdej strony. Ten ostatni krok jest o wiele łatwiejszy do wykonania w języku programowania innym niż T-SQL. Zauważ, że DBCC PAGE będzie działać, nawet jeśli obiekt podstawowy nie jest normalnie czytelny z połączenia innego niż DAC. Jeśli strona nie znajduje się w pamięci, zostanie normalnie odczytana z pamięci trwałej.

Dodatkowy wysiłek, aby uniknąć wymogu DAC, opłaca się, umożliwiając wielu użytkownikom jednoczesne korzystanie z procesu odszyfrowywania. W tym artykule użyję podejścia DAC ze względu na prostotę i przestrzeń.

Przykład praktyczny

Poniższy kod tworzy testową zaszyfrowaną funkcję skalarną:

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;

Pełna implementacja deszyfrowania znajduje się poniżej. Jedynym parametrem, który wymaga zmiany, aby działał z innymi obiektami, jest wartość początkowa @objectid ustawić w pierwszym DECLARE oświadczenie.

-- *** 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
        )
    );

Zwróć uwagę na ostateczną konwersję do nvarchar ponieważ tekst modułu jest wpisywany jako nvarchar(max) .

Dane wyjściowe to:

Wniosek

Powody, dla których metoda opisana we wstępie działa:

  • SQL Server używa szyfru strumieniowego RC4 do odwracalnie wyłącznego lub tekstu źródłowego.
  • Klucz RC4 zależy tylko od identyfikatora rodziny bazy danych, identyfikatora obiektu i podobjidu.
  • Tymczasowe zastąpienie tekstu modułu oznacza wygenerowanie tego samego (zaszyfrowanego SHA-1) klucza RC4.
  • Z tym samym kluczem, ten sam strumień RC4 jest generowany, umożliwiając wyłączność lub odszyfrowanie.

Użytkownicy, którzy nie mają dostępu do tabel systemowych, plików baz danych lub innego dostępu na poziomie administratora, nie mogą pobierać zaszyfrowanego tekstu modułu. Ponieważ sam SQL Server musi być w stanie odszyfrować moduł, nie ma sposobu, aby uniemożliwić odpowiednio uprzywilejowanym użytkownikom robienie tego samego.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Łączenie SQuirreL SQL z Microsoft Excel

  2. Czytelne materiały wtórne w ramach budżetu

  3. Podejścia do bezpieczeństwa w modelowaniu danych. Część 4

  4. StarJoinInfo w planach wykonania

  5. Jak maskować tabele i zachować integralność referencyjną?