Database
 sql >> Teknologi Basis Data >  >> RDS >> Database

Bagian Dalam DENGAN ENKRIPSI

Cukup mudah bagi administrator SQL Server untuk memulihkan teks prosedur tersimpan, tampilan, fungsi, dan pemicu yang dilindungi menggunakan WITH ENCRYPTION . Banyak artikel telah ditulis tentang ini, dan beberapa alat komersial tersedia. Garis besar dasar dari metode umum adalah untuk:

  1. Dapatkan formulir terenkripsi (A) menggunakan Koneksi Administrator Khusus.
  2. Mulai transaksi.
  3. Ganti definisi objek dengan teks yang diketahui (B) setidaknya sama panjangnya dengan aslinya.
  4. Dapatkan formulir terenkripsi untuk teks yang dikenal (C).
  5. Mengembalikan transaksi untuk meninggalkan objek target dalam keadaan awalnya.
  6. Dapatkan dokumen asli yang tidak dienkripsi dengan menerapkan eksklusif-atau untuk setiap karakter:A XOR (B XOR C)

Itu semua cukup mudah, tetapi tampaknya sedikit seperti sulap:Ini tidak menjelaskan banyak tentang bagaimana dan mengapa itu bekerja . Artikel ini mencakup aspek tersebut bagi Anda yang menganggap detail semacam ini menarik, dan memberikan metode alternatif untuk dekripsi yang lebih menggambarkan prosesnya.

Stream Cipher

Algoritma enkripsi yang mendasari yang digunakan SQL Server untuk enkripsi modul adalah sandi aliran RC4™. Garis besar proses enkripsi adalah:

  1. Inisialisasi sandi RC4 dengan kunci kriptografik.
  2. Hasilkan aliran byte pseudorandom.
  3. Gabungkan teks biasa modul dengan aliran byte menggunakan eksklusif-or.

Kita dapat melihat proses ini terjadi menggunakan debugger dan simbol publik. Misalnya, pelacakan tumpukan di bawah ini menunjukkan SQL Server menginisialisasi kunci RC4 sambil bersiap untuk mengenkripsi teks modul:

Berikut ini menunjukkan SQL Server mengenkripsi teks menggunakan aliran byte pseudorandom RC4:

Seperti kebanyakan stream cipher, proses dekripsi sama dengan enkripsi, memanfaatkan fakta bahwa eksklusif-atau reversibel (A XOR B XOR B = A ).

Penggunaan stream cipher adalah alasan eksklusif-atau digunakan dalam metode yang dijelaskan di awal artikel. Tidak ada yang tidak aman dalam menggunakan eksklusif-atau, asalkan metode enkripsi aman digunakan, kunci inisialisasi dirahasiakan, dan kunci tidak digunakan kembali.

RC4 tidak terlalu kuat, tapi itu bukan masalah utama di sini. Meskipun demikian, perlu dicatat bahwa enkripsi menggunakan RC4 secara bertahap dihapus dari SQL Server, dan tidak digunakan lagi (atau dinonaktifkan, bergantung pada versi dan tingkat kompatibilitas database) untuk operasi pengguna seperti membuat kunci simetris.

Kunci Inisialisasi RC4

SQL Server menggunakan tiga informasi untuk menghasilkan kunci yang digunakan untuk menginisialisasi stream cipher RC4:

  1. GUD keluarga basis data.

    Ini dapat diperoleh dengan paling mudah dengan menanyakan sys.database_recovery_status . Itu juga terlihat dalam perintah tidak berdokumen seperti DBCC DBINFO dan DBCC DBTABLE .

  2. ID objek modul target.

    Ini hanya ID objek yang sudah dikenal. Perhatikan bahwa tidak semua modul yang mengizinkan enkripsi memiliki cakupan skema. Anda harus menggunakan tampilan metadata (sys.triggers atau sys.server_triggers ) untuk mendapatkan ID objek untuk DDL dan pemicu cakupan server, bukan sys.objects atau OBJECT_ID , karena ini hanya berfungsi dengan objek cakupan skema.

  3. ID sub-objek modul target.

    Ini adalah nomor prosedur untuk prosedur tersimpan bernomor. Ini adalah 1 untuk prosedur tersimpan yang tidak bernomor, dan nol untuk semua kasus lainnya.

Menggunakan debugger lagi, kita dapat melihat GUID keluarga diambil selama inisialisasi kunci:

GUID keluarga basis data diketik pengidentifikasi unik , ID objek adalah bilangan bulat , dan ID sub-objek adalah smallint .

Setiap bagian dari kunci harus dikonversi ke format biner tertentu. Untuk GUID keluarga basis data, mengonversi pengidentifikasi unik ketik ke biner(16) menghasilkan representasi biner yang benar. Kedua ID harus dikonversi ke biner dalam representasi little-endian (byte paling tidak signifikan terlebih dahulu).

Catatan: Berhati-hatilah untuk tidak secara tidak sengaja memberikan GUID sebagai string! Itu harus diketik pengidentifikasi unik .

Cuplikan kode di bawah ini menunjukkan operasi konversi yang benar untuk beberapa nilai sampel:

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

Langkah terakhir untuk menghasilkan kunci inisialisasi RC4 adalah menggabungkan tiga nilai biner di atas menjadi satu biner (22), dan menghitung hash SHA-1 dari hasilnya:

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

Untuk contoh data yang diberikan di atas, kunci inisialisasi terakhir adalah:

0x6C914908E041A08DD8766A0CFEDC113585D69AF8

Kontribusi ID objek dan sub-objek modul target ke hash SHA-1 sulit dilihat dalam satu tangkapan layar debugger, tetapi pembaca yang tertarik dapat merujuk ke pembongkaran sebagian initspkey di bawah ini:

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 dan SHAUpdate panggilan menambahkan komponen ke hash SHA, yang akhirnya dihitung dengan panggilan ke SHAFinal .

SHAINit panggilan menyumbang 10 jam byte (16 desimal) disimpan di [rsp+40 jam], yang merupakan GUD keluarga . SHAUpdate pertama panggilan menambahkan 4 byte (seperti yang ditunjukkan dalam register r8d), disimpan di [rsp+24h], yang merupakan objek PENGENAL. SHAUpdate kedua panggilan menambahkan 2 byte, disimpan di [rsp+20h], yang merupakan subobjid .

Instruksi terakhir meneruskan hash SHA-1 yang dihitung ke rutinitas inisialisasi kunci RC4 rc4_key . Panjang hash disimpan dalam register edx:14h (20 desimal) byte, yang merupakan panjang hash yang ditentukan untuk SHA dan SHA-1 (160 bit).

Implementasi RC4

Algoritma inti RC4 terkenal, dan relatif sederhana. Akan lebih baik diimplementasikan dalam bahasa .Net untuk alasan efisiensi dan kinerja, tetapi ada implementasi T-SQL di bawah ini.

Kedua fungsi T-SQL ini mengimplementasikan algoritme penjadwalan kunci RC4 dan generator nomor pseudorandom, dan awalnya ditulis oleh SQL Server MVP Peter Larsson. Saya telah membuat beberapa modifikasi kecil untuk sedikit meningkatkan kinerja, dan memungkinkan binari panjang LOB dikodekan dan didekodekan. Bagian dari proses ini dapat diganti dengan implementasi RC4 standar.

/*
** 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

Teks Modul Terenkripsi

Cara termudah bagi administrator SQL Server untuk mendapatkannya adalah dengan membaca varbinary(max) nilai yang disimpan di imageval kolom sys.sysobjvalues , yang hanya dapat diakses melalui Koneksi Administrator Khusus (DAC).

Ini adalah ide yang sama dengan metode rutin yang dijelaskan dalam pendahuluan, meskipun kami menambahkan filter pada valclass =1. Tabel internal ini juga merupakan tempat yang nyaman untuk mendapatkan subobjid . Jika tidak, kita perlu memeriksa sys.numbered_procedures ketika objek target adalah prosedur, gunakan 1 untuk prosedur tak bernomor, atau nol untuk hal lain, seperti yang dijelaskan sebelumnya.

Dimungkinkan untuk menghindari penggunaan DAC dengan membaca imageval dari sys.sysobjvalues secara langsung, menggunakan beberapa DBCC PAGE panggilan. Ini melibatkan sedikit lebih banyak pekerjaan untuk menemukan halaman dari metadata, ikuti imageval rantai LOB, dan baca data biner target dari setiap halaman. Langkah terakhir jauh lebih mudah dilakukan dalam bahasa pemrograman selain T-SQL. Perhatikan bahwa DBCC PAGE akan berfungsi, meskipun objek dasar biasanya tidak dapat dibaca dari koneksi non-DAC. Jika halaman tidak ada dalam memori, halaman akan dibaca dari penyimpanan persisten seperti biasa.

Upaya ekstra untuk menghindari persyaratan DAC terbayar dengan memungkinkan banyak pengguna untuk menggunakan proses dekripsi secara bersamaan. Saya akan menggunakan pendekatan DAC dalam artikel ini untuk alasan kesederhanaan dan ruang.

Contoh Kerja

Kode berikut membuat fungsi skalar terenkripsi uji:

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;

Implementasi dekripsi lengkap di bawah ini. Satu-satunya parameter yang perlu diubah agar berfungsi untuk objek lain adalah nilai awal @objectid atur di DECLARE first pertama pernyataan.

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

Perhatikan konversi terakhir ke nvarchar karena teks modul diketik sebagai nvarchar(max) .

Outputnya adalah:

Kesimpulan

Alasan metode yang dijelaskan dalam pendahuluan berhasil adalah:

  • SQL Server menggunakan RC4 stream cipher untuk reversibel eksklusif-atau teks sumber.
  • Kunci RC4 hanya bergantung pada panduan keluarga database, id objek, dan subobjid.
  • Mengganti teks modul untuk sementara berarti kunci RC4 (hash SHA-1) yang sama dihasilkan.
  • Dengan kunci yang sama, aliran RC4 yang sama dihasilkan, memungkinkan dekripsi atau eksklusif.

Pengguna yang tidak memiliki akses ke tabel sistem, file database, atau akses tingkat admin lainnya, tidak dapat mengambil teks modul terenkripsi. Karena SQL Server sendiri harus dapat mendekripsi modul, tidak ada cara untuk mencegah pengguna dengan hak istimewa yang sesuai untuk melakukan hal yang sama.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Tingkat Isolasi Baca yang Dapat Diulang

  2. Apa itu NoSQL dan Bagaimana Cara Memanfaatkannya?

  3. Kerentanan Injeksi SQL Joomla

  4. Menyelami NoSQL:Daftar lengkap database NoSQL

  5. 911/112:Model Data Layanan Panggilan Darurat