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:
- Dapatkan formulir terenkripsi (A) menggunakan Koneksi Administrator Khusus.
- Mulai transaksi.
- Ganti definisi objek dengan teks yang diketahui (B) setidaknya sama panjangnya dengan aslinya.
- Dapatkan formulir terenkripsi untuk teks yang dikenal (C).
- Mengembalikan transaksi untuk meninggalkan objek target dalam keadaan awalnya.
- 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:
- Inisialisasi sandi RC4 dengan kunci kriptografik.
- Hasilkan aliran byte pseudorandom.
- 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:
- 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
danDBCC DBTABLE
. - 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. - 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.