sql >> データベース >  >> RDS >> Database

WITHENCRYPTIONの内部

    SQL Server管理者は、WITH ENCRYPTIONを使用して保護されたストアドプロシージャ、ビュー、関数、およびトリガーのテキストを復元するのは非常に簡単です。 。これについては多くの記事が書かれており、いくつかの商用ツールが利用可能です。一般的な方法の基本的な概要は次のとおりです。

    1. 専用管理者接続を使用して暗号化されたフォーム(A)を取得します。
    2. トランザクションを開始します。
    3. オブジェクト定義を、少なくとも元のテキストと同じ長さの既知のテキスト(B)に置き換えます。
    4. 既知のテキストの暗号化されたフォームを取得します(C)。
    5. トランザクションをロールバックして、ターゲットオブジェクトを初期状態のままにします。
    6. 排他的論理和を各文字に適用して、暗号化されていないオリジナルを取得します:A XOR (B XOR C)

    これはすべて非常に簡単ですが、魔法のように見えます。どのように、なぜ機能するのかについてはあまり説明されていません。 。この記事では、この種の詳細が興味深いと感じた方のためにその側面を取り上げ、プロセスをより詳しく説明する復号化の代替方法を提供します。

    ストリーム暗号

    SQLServerがモジュール暗号化に使用する基盤となる暗号化アルゴリズム RC4™ストリーム暗号です。暗号化プロセスの概要は次のとおりです。

    1. 暗号化キーを使用してRC4暗号を初期化します。
    2. バイトの疑似ランダムストリームを生成します。
    3. 排他的論理和を使用して、モジュールのプレーンテキストをバイトストリームと結合します。

    このプロセスは、デバッガーとパブリックシンボルを使用して発生していることがわかります。たとえば、以下のスタックトレースは、モジュールテキストの暗号化の準備中にSQLServerがRC4キーを初期化することを示しています。

    次の例は、SQLServerがRC4疑似ランダムバイトストリームを使用してテキストを暗号化する方法を示しています。

    ほとんどのストリーム暗号と同様に、復号化のプロセスは暗号化と同じであり、排他的論理和(A XOR B XOR B = A)という事実を利用します。 。

    ストリーム暗号の使用が排他的論理和の理由です 記事の冒頭で説明した方法で使用されます。排他的論理和を使用することについて本質的に危険なことは何もありません。または、安全な暗号化方式が使用され、初期化キーが秘密にされ、キーが再利用されない場合に限ります。

    RC4は特に強力ではありませんが、それはここでの主要な問題ではありません。とはいえ、RC4を使用した暗号化はSQL Serverから徐々に削除されており、対称鍵の作成などのユーザー操作では非推奨になっています(バージョンやデータベースの互換性レベルによっては無効になっています)。

    RC4初期化キー

    SQL Serverは、3つの情報を使用して、RC4ストリーム暗号の初期化に使用されるキーを生成します。

    1. データベースファミリGUID。

      これは、 sys.database_recovery_statusにクエリを実行することで最も簡単に取得できます。 。 DBCC DBINFOなどの文書化されていないコマンドでも表示されます およびDBCC DBTABLE

    2. ターゲットモジュールのオブジェクトID。

      これはおなじみのオブジェクトIDです。暗号化を許可するすべてのモジュールがスキーマスコープであるとは限らないことに注意してください。メタデータビュー( sys.triggers )を使用する必要があります またはsys.server_triggers sys.objects ではなく、DDLおよびサーバースコープのトリガーのオブジェクトIDを取得する またはOBJECT_ID 、これらはスキーマスコープのオブジェクトでのみ機能するためです。

    3. ターゲットモジュールのサブオブジェクトID。

      これは、番号付きストアドプロシージャのプロシージャ番号です。番号のないストアドプロシージャの場合は1、それ以外の場合は0です。

    デバッガーを再度使用すると、キーの初期化中にファミリーGUIDが取得されていることがわかります。

    データベースファミリGUIDは、 uniqueidentifierと入力されます。 、オブジェクトIDは integer 、サブオブジェクトIDは smallint

    キーの各部分は必須 特定のバイナリ形式に変換されます。データベースファミリGUIDの場合、 uniqueidentifierを変換します binary(16)と入力します 正しいバイナリ表現を生成します。 2つのIDは、リトルエンディアン表現でバイナリに変換する必要があります(最下位バイトが最初)。

    注: 誤って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初期化キーを生成する最後のステップは、上記の3つのバイナリ値を1つのbinary(22)に連結し、結果のSHA-1ハッシュを計算することです。

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

    上記のサンプルデータの場合、最終的な初期化キーは次のとおりです。

    0x6C914908E041A08DD8766A0CFEDC113585D69AF8

    ターゲットモジュールのオブジェクトIDとサブオブジェクトIDの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 およびSHAUpdate 呼び出しは、SHAハッシュにコンポーネントを追加します。これは、最終的に SHAFinalの呼び出しによって計算されます。 。

    SHAInit 呼び出しは、ファミリGUIDである[rsp+40h]に格納されている10hバイト(10進数で16)を提供します 。最初のSHAUpdate callは、オブジェクトである[rsp+ 24h]に格納されている4バイトを追加します(r8dレジスタに示されています)。 ID。 2番目のSHAUpdate callは、 subobjidである[rsp+20h]に格納されている2バイトを追加します 。

    最後の命令は、計算されたSHA-1ハッシュをRC4キー初期化ルーチン rc4_keyに渡します。 。ハッシュの長さはレジスタedxに格納されます:14h(10進数の20)バイト。これは、SHAおよびSHA-1(160ビット)に対して定義されたハッシュ長です。

    RC4の実装

    コアRC4アルゴリズムはよく知られており、比較的単純です。効率とパフォーマンスの理由から、.Net言語で実装する方が適切ですが、以下にT-SQLの実装があります。

    これらの2つのT-SQL関数は、RC4キースケジューリングアルゴリズムと疑似乱数ジェネレーターを実装し、元々はSQLServerMVPのPeterLarssonによって作成されました。パフォーマンスを少し改善し、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.この内部テーブルは、 subobjidを取得するのにも便利な場所です。 。それ以外の場合は、 sys.numbered_proceduresを確認する必要があります。 ターゲットオブジェクトがプロシージャの場合、前述のように、番号のないプロシージャには1を使用し、それ以外の場合は0を使用します。

    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 Serverは、RC4ストリーム暗号を使用して、可逆的に排他的、またはソーステキストを使用します。
    • RC4キーは、データベースファミリのGUID、オブジェクトID、およびサブオブジェクトにのみ依存します。
    • モジュールテキストを一時的に置き換えると、同じ(SHA-1ハッシュ)RC4キーが生成されます。
    • 同じキーを使用して、同じRC4ストリームが生成され、排他的論理和または復号化が可能になります。

    システムテーブル、データベースファイル、またはその他の管理者レベルのアクセス権を持たないユーザーは、暗号化されたモジュールテキストを取得できません。 SQL Server自体がモジュールを復号化できる必要があるため、適切な特権を持つユーザーが同じことを行うのを防ぐ方法はありません。


    1. 2番目に高い給料の従業員を見つける方法は?

    2. sqliteLog 14:行でファイルを開くことができません

    3. 結果が得られるまで複数のSELECTを試す方法はありますか?

    4. SQLServerのジョブ履歴を取得する4つの方法