Kode ini berfungsi dengan baik karena:
- Parameter, dan
- Tidak melakukan Dynamic SQL
Agar SQL Injection berfungsi, Anda harus membuat string kueri (yang tidak Anda lakukan) dan tidak menerjemahkan apostrof tunggal ( '
) ke dalam escaped-apostrophes ( ''
) (yang diloloskan melalui parameter input).
Dalam upaya Anda untuk memberikan nilai "kompromi", 'Male; DROP TABLE tblActor'
string itu hanya itu, string biasa.
Sekarang, jika Anda melakukan sesuatu seperti:
DECLARE @SQL NVARCHAR(MAX);
SET @SQL = N'SELECT fields FROM table WHERE field23 = '
+ @InputParam;
EXEC(@SQL);
maka yang akan rentan terhadap SQL Injection karena itu permintaan tidak di saat ini, konteks pra-parsing; permintaan itu hanyalah string lain saat ini. Jadi nilai @InputParam
bisa '2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;
dan yang mungkin menimbulkan masalah karena permintaan itu akan diberikan, dan dieksekusi, seperti:
SELECT fields FROM table WHERE field23 = '2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;
Ini adalah salah satu (dari beberapa) alasan utama untuk menggunakan Prosedur Tersimpan: secara inheren lebih aman (well, selama Anda tidak menghindari keamanan itu dengan membangun kueri seperti yang saya perlihatkan di atas tanpa memvalidasi nilai dari parameter apa pun yang digunakan). Meskipun jika Anda perlu membangun SQL Dinamis, cara yang lebih disukai adalah membuat parameter itu juga menggunakan sp_executesql
:
DECLARE @SQL NVARCHAR(MAX);
SET @SQL = N'SELECT fields FROM table WHERE field23 = @SomeDate_tmp';
EXEC sp_executesql
@SQL,
N'SomeDate_tmp DATETIME',
@SomeDate_tmp = @InputParam;
Dengan menggunakan pendekatan ini, seseorang yang mencoba '2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;
untuk DATETIME
memasukkan parameter input akan mendapatkan kesalahan saat menjalankan Prosedur Tersimpan. Atau bahkan jika Prosedur Tersimpan diterima @InputParameter
sebagai NVARCHAR(100)
, itu harus dikonversi ke DATETIME
dalam untuk masuk ke sp_executesql
panggilan itu. Dan bahkan jika parameter dalam SQL Dinamis adalah tipe string, masuk ke Prosedur Tersimpan di tempat pertama setiap tanda kutip otomatis akan lolos ke tanda kutip ganda.
Ada jenis serangan yang kurang dikenal di mana penyerang mencoba untuk mengisi bidang input dengan tanda kutip sedemikian sehingga string di dalam Prosedur Tersimpan yang akan digunakan untuk membangun SQL Dinamis tetapi yang dinyatakan terlalu kecil tidak dapat memuat semuanya dan mendorong tanda kutip akhir dan entah bagaimana berakhir dengan jumlah tanda kutip yang benar sehingga tidak lagi "melarikan diri" dalam string. Ini disebut Pemotongan SQL dan dibicarakan dalam artikel majalah MSDN berjudul "Serangan Pemotongan SQL Baru dan Cara Menghindarinya", oleh Bala Neerumalla, tetapi artikel itu tidak lagi online. Masalah yang mengandung artikel ini - edisi November, 2006 MSDN Magazine - hanya tersedia sebagai file Bantuan Windows (dalam .chmformat). Jika Anda mengunduhnya, itu mungkin tidak terbuka karena pengaturan keamanan default. Jika ini terjadi, klik kanan pada file MSDNMagazineNovember2006en-us.chm dan pilih "Properties". Di salah satu tab itu akan ada opsi untuk "Percayai jenis file ini" (atau sesuatu seperti itu) yang perlu diperiksa / diaktifkan. Klik tombol "OK" dan kemudian coba buka kembali file .chm .
Variasi lain dari serangan Truncation adalah, dengan asumsi variabel lokal digunakan untuk menyimpan nilai "aman" yang disediakan pengguna karena setiap tanda kutip ganda digandakan sehingga dapat diloloskan, untuk mengisi variabel lokal itu dan menempatkan tanda kutip tunggal pada akhirnya. Idenya di sini adalah bahwa jika variabel lokal tidak berukuran benar, tidak akan ada cukup ruang di akhir untuk penawaran tunggal kedua, biarkan variabel diakhiri dengan penawaran tunggal tunggal yang kemudian digabungkan dengan penawaran tunggal yang mengakhiri nilai literal dalam SQL Dinamis, mengubah kutip tunggal yang berakhir menjadi kutipan tunggal yang diloloskan, dan string literal dalam Dynamic SQL kemudian diakhiri dengan kutipan-tunggal berikutnya yang dimaksudkan untuk memulai string literal berikutnya. Sebagai contoh:
-- Parameters:
DECLARE @UserID INT = 37,
@NewPassword NVARCHAR(15) = N'Any Value ....''',
@OldPassword NVARCHAR(15) = N';Injected SQL--';
-- Stored Proc:
DECLARE @SQL NVARCHAR(MAX),
@NewPassword_fixed NVARCHAR(15) = REPLACE(@NewPassword, N'''', N''''''),
@OldPassword_fixed NVARCHAR(15) = REPLACE(@OldPassword, N'''', N'''''');
SELECT @NewPassword AS [@NewPassword],
REPLACE(@NewPassword, N'''', N'''''') AS [REPLACE output],
@NewPassword_fixed AS [@NewPassword_fixed];
/*
@NewPassword REPLACE output @NewPassword_fixed
Any Value ....' Any Value ....'' Any Value ....'
*/
SELECT @OldPassword AS [@OldPassword],
REPLACE(@OldPassword, N'''', N'''''') AS [REPLACE output],
@OldPassword_fixed AS [@OldPassword_fixed];
/*
@OldPassword REPLACE output @OldPassword_fixed
;Injected SQL-- ;Injected SQL-- ;Injected SQL--
*/
SET @SQL = N'UPDATE dbo.TableName SET [Password] = N'''
+ @NewPassword_fixed + N''' WHERE [TableNameID] = '
+ CONVERT(NVARCHAR(10), @UserID) + N' AND [Password] = N'''
+ @OldPassword_fixed + N''';';
SELECT @SQL AS [Injected];
Di sini, Dynamic SQL yang akan dieksekusi sekarang:
UPDATE dbo.TableName SET [Password] = N'Any Value ....'' WHERE [TableNameID] = 37 AND [Password] = N';Injected SQL--';
Dynamic SQL yang sama, dalam format yang lebih mudah dibaca, adalah:
UPDATE dbo.TableName SET [Password] = N'Any Value ....'' WHERE [TableNameID] = 37 AND [Password] = N';
Injected SQL--';
Memperbaiki ini mudah. Lakukan saja salah satu dari yang berikut:
- JANGAN MENGGUNAKAN DINAMIS SQL KECUALI TANPA KEBUTUHAN! (Saya mendaftar ini pertama karena itu benar-benar harus menjadi hal pertama yang harus dipertimbangkan).
- Ukuran yang benar variabel lokal (yaitu harus dua kali ukuran sebagai parameter input, kalau-kalau semua karakter yang lewat adalah tanda kutip tunggal.
Jangan gunakan variabel lokal untuk menyimpan nilai "tetap"; cukup masukkan REPLACE()
langsung ke dalam penciptaan Dynamic SQL:
SET @SQL = N'UPDATE dbo.TableName SET [Password] = N'''
+ REPLACE(@NewPassword, N'''', N'''''') + N''' WHERE [TableNameID] = '
+ CONVERT(NVARCHAR(10), @UserID) + N' AND [Password] = N'''
+ REPLACE(@OldPassword, N'''', N'''''') + N''';';
SELECT @SQL AS [No SQL Injection here];
Dynamic SQL tidak lagi dikompromikan:
UPDATE dbo.TableName SET [Password] = N'Any Value ....''' WHERE [TableNameID] = 37 AND [Password] = N';Injected SQL--';
Catatan tentang contoh Trunction di atas:
- Ya, ini adalah contoh yang sangat dibuat-buat. Tidak banyak yang bisa dilakukan hanya dalam 15 karakter untuk disuntikkan. Tentu, mungkin
DELETE tableName
menjadi destruktif, tetapi kecil kemungkinannya untuk menambahkan pengguna pintu belakang, atau mengubah kata sandi admin.
- Jenis serangan ini mungkin membutuhkan pengetahuan tentang kode, nama tabel, dll. Lebih sedikit kemungkinan dilakukan oleh orang asing / skrip-kiddie acak, tetapi saya memang bekerja di tempat yang diserang oleh mantan karyawan yang agak kesal yang mengetahui kerentanan. di satu halaman web tertentu yang tidak diketahui orang lain. Artinya, terkadang penyerang memiliki pengetahuan yang mendalam tentang sistem.
- Tentu, mengatur ulang kata sandi semua orang kemungkinan akan diselidiki, yang mungkin memberi tahu perusahaan bahwa ada serangan, tetapi mungkin masih menyediakan cukup waktu untuk menyuntikkan pengguna pintu belakang atau mungkin mendapatkan info sekunder untuk digunakan / dieksploitasi nanti.
- Sekalipun skenario ini sebagian besar bersifat akademis (artinya tidak mungkin terjadi di dunia nyata), itu tetap tidak mustahil.
Untuk informasi lebih rinci terkait dengan SQL Injection (mencakup berbagai RDBMS dan skenario), silakan lihat yang berikut dari Proyek Keamanan Aplikasi Web Terbuka (OWASP):
Pengujian untuk SQL Injection
Terkait Stack Overflow jawaban pada SQL Injection dan SQL Truncation:
Seberapa amankah T-SQL setelah Anda mengganti karakter 'escape?
EXEC usp_actorBirthdays 'Tom', 'Male''; DROP TABLE tblActor'