Bu kod şu şekilde düzgün çalışıyor:
- Parametreli ve
- Değil herhangi Dinamik SQL yapıyor
İşe SQL Enjeksiyon için, size (yapmadığınız) bir sorgu dizesi oluşturmak ve gerek olmayan çevirmek tek kesme ( '
kaçarak-kesme işareti içine) ( ''
) (bu giriş parametreleri aracılığıyla öncelenir).
"Güvenliği ihlal edilmiş" bir değeri aktarmaya çalıştığınızda, 'Male; DROP TABLE tblActor'
dize tam da budur.
Şimdi, eğer şu çizgilerle bir şeyler yapıyorsanız:
DECLARE @SQL NVARCHAR(MAX);
SET @SQL = N'SELECT fields FROM table WHERE field23 = '
+ @InputParam;
EXEC(@SQL);
Sonra o çünkü SQL Enjeksiyon duyarlı olacağını o sorgu akımı, önceden çözümlü bağlam içinde değildir; bu sorgu şu anda başka bir dizedir. Yani değeri @InputParam
olabilir '2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;
ve bu bir sorun oluşturabilir, çünkü bu sorgu şu şekilde işlenir ve yürütülür:
SELECT fields FROM table WHERE field23 = '2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;
Bu, Saklı Yordamları kullanmanın başlıca nedenlerinden biridir: doğal olarak daha güvenlidir (kullanılan parametrelerin değerlerini doğrulamadan yukarıda gösterdiğim gibi sorgular oluşturarak bu güvenliği atlatmazsanız). Dinamik SQL oluşturmanız gerekiyorsa, tercih edilen yol bunu kullanarak parametrelendirmektir 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;
Bu yaklaşımı kullanarak, '2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;
bir DATETIME
giriş parametresi için geçmeye çalışan biri Saklı Yordam yürütülürken hata alır. Veya Saklı Yordam kabul bile @InputParameter
olarak NVARCHAR(100)
, bir karşı dönüştürmek zorunda kalacak DATETIME
o içine geçmek için sp_executesql
çağrı. Ve Dinamik SQL'deki parametre bir dize türü olsa bile, en başta Saklı Yordam'a gelen herhangi bir tek kesme işareti otomatik olarak çift kesme işaretine kaçar.
Saldırganın giriş alanını kesme işaretleriyle doldurmaya çalıştığı daha az bilinen bir saldırı türü vardır, böylece Saklı Yordamın içindeki Dinamik SQL'i oluşturmak için kullanılacak ancak çok küçük olduğu bildirilen bir şey her şeye sığmaz ve bitiş kesme işaretini dışarı iter ve dizgide artık "kaçmayacak" şekilde bir şekilde doğru kesme işareti ile biter. Buna SQL Truncation denir ve Bala Neerumalla tarafından "Yeni SQL Truncation Saldırıları ve Onlardan Nasıl Korunur" başlıklı bir MSDN dergisinde bahsedildi, ancak makale artık çevrimiçi değil. Bu makaleyi içeren sorun - MSDN Magazine'in Kasım 2006 sürümü - yalnızca Windows Yardım dosyası ( .chm biçiminde) olarak kullanılabilirbiçim). İndirirseniz, varsayılan güvenlik ayarları nedeniyle açılmayabilir. Bu durumda, MSDNMagazineNovember2006en-us.chm dosyasına sağ tıklayın ve "Özellikler" i seçin. Bu sekmelerden birinde, kontrol edilmesi / etkinleştirilmesi gereken "Bu dosya türüne güven" (veya bunun gibi bir şey) seçeneği bulunur. "Tamam" düğmesini tıklatın ve sonra .chm dosyasını yeniden açmayı deneyin .
Truncation saldırısının bir başka varyasyonu, yerel değişkenin "güvenli" kullanıcı tarafından sağlanan değeri saklamak için kullanıldığını varsayarsak, kaçmak için herhangi bir tek tırnak bulunduğundan, bu yerel değişkeni doldurmak ve tek tırnak koymak sonunda. Buradaki fikir, yerel değişken düzgün bir şekilde boyutlandırılmadıysa, sonunda ikinci tek tırnak için yeterli alan kalmayacağı, değişkeni tek bir tırnak ile biten ve ardından tek tırnak ile birleştiren Dinamik SQL'deki değişmez değeri sonlandırır ve bu biten tek tırnak işaretini gömülü bir tek tırnak içine dönüştürür ve Dinamik SQL'deki dize değişmez değeri, bir sonraki dize değişmezine başlaması amaçlanan bir sonraki tek tırnakla biter. Örneğin:
-- 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];
Burada, yürütülecek Dinamik SQL şu şekildedir:
UPDATE dbo.TableName SET [Password] = N'Any Value ....'' WHERE [TableNameID] = 37 AND [Password] = N';Injected SQL--';
Aynı Dinamik SQL, daha okunabilir bir biçimde:
UPDATE dbo.TableName SET [Password] = N'Any Value ....'' WHERE [TableNameID] = 37 AND [Password] = N';
Injected SQL--';
Bunu düzeltmek kolaydır. Sadece aşağıdakilerden birini yapın:
- DİNAMİK SQL KESİNLİKLE KESİNLİKLE GEREKLİ OLMAYIN! (Bunu ilk olarak listeliyorum çünkü gerçekten dikkate alınması gereken ilk şey olmalı).
- Yerel değişkenin uygun şekilde boyutlandırılması (yani, geçirilen tüm karakterlerin tek tırnak olması durumunda, girdi parametresinin iki katı boyutta olması gerekir.
"Sabit" değeri depolamak için yerel bir değişken kullanmayın; REPLACE()
doğrudan Dinamik SQL'in oluşturulmasına koymanız yeterlidir:
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];
Dinamik SQL'den artık ödün verilmiyor:
UPDATE dbo.TableName SET [Password] = N'Any Value ....''' WHERE [TableNameID] = 37 AND [Password] = N';Injected SQL--';
Yukarıdaki Trunction örneği hakkında notlar:
- Evet, bu çok tartışmalı bir örnek. Enjekte etmek için sadece 15 karakterde yapılabilecek çok şey yok. Elbette, a
DELETE tableName
yıkıcı olabilir, ancak arka kapı kullanıcısı ekleme veya yönetici şifresini değiştirme olasılığı daha düşüktür.
- Bu tür bir saldırı muhtemelen kod, tablo adları vb. Hakkında bilgi gerektirir. Rastgele yabancı / script-kiddie tarafından yapılması daha az olasıdır, ancak bir güvenlik açığı bilen oldukça üzgün eski bir çalışanın saldırdığı bir yerde çalıştım. Kimsenin farkında olmadığı belirli bir web sayfasında. Yani, bazen saldırganlar sistem hakkında samimi bilgiye sahip olurlar.
- Elbette, herkesin şifresini sıfırlamanın araştırılması muhtemeldir, bu da şirketi bir saldırı olduğunu iddia edebilir, ancak yine de arka kapı kullanıcısına enjekte etmek veya daha sonra kullanmak / kullanmak için ikincil bilgi almak için yeterli zaman sağlayabilir.
- Bu senaryo çoğunlukla akademik olsa bile (yani gerçek dünyada olması muhtemel değildir), yine de imkansız değildir.
SQL Injection ile ilgili daha ayrıntılı bilgi için (çeşitli RDBMS ve senaryoları kapsar), lütfen Açık Web Uygulaması Güvenlik Projesi'nden (OWASP) aşağıdakilere bakın :
SQL Injection Testi
İlgili Enjeksiyon Taşması SQL Enjeksiyonu ve SQL Truncation cevabı:
'kaçış karakterini değiştirdikten sonra T-SQL ne kadar güvenli?
EXEC usp_actorBirthdays 'Tom', 'Male''; DROP TABLE tblActor'