Etrafta alır mysql_real_escape_string()SQL enjeksiyon
mysql_real_escape_string()
fonksiyonu kullanarak bir SQL enjeksiyon olasılığı var mı?
Bu örnek durumu göz önünde bulundurun. SQL bu gibi PHP ile inşa edilmiştir:
$login = mysql_real_escape_string(GetFromPost('login'));
$password = mysql_real_escape_string(GetFromPost('password'));
$sql = "SELECT * FROM table WHERE login='$login' AND password='$password'";
Pek çok kişi hala tehlikeli ve mümkün mysql_real_escape_string()
işlevi bile kesmek gibi bir kod benim için söylediklerini duydum. Ama ben herhangi bir olası istismar düşünüyorsun?
Bu gibi klasik enjeksiyon:
aaa' OR 1=1 --
çalışmıyor.
PHP kodu ile alacağı Olası enjeksiyon yukarıdaki biliyor musunuz?
CEVAP
Kısa cevapEvet, Evet mysql_real_escape_string()
aşmanın bir yolu var.
Çok BELİRSİZ KENAR DURUMLAR için!!!
Uzun cevap o kadar kolay değil. Bir saldırı demonstrated here dayanıyor.
Saldırı
Bakalım saldırı göstererek başlamak...
mysql_query('SET NAMES gbk');
$var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*");
mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");
Bazı durumlarda, bu 1'den fazla satır döndürür. Bakalım burada neler olduğunu incelemek:
Bir Karakter seçerek Ayarlayın
mysql_query('SET NAMES gbk');
Bu saldırı için,
0x27
yani sunucu bağlantısı ASCII olarak'
kodlamak için, hem de bekliyor kodlamayı ihtiyacımız varve16 *yani* 17 ** bir ASCII olan bir karakter var. Görünüşe göre, orada varsayılan olarak 5 tür kodlamalar MySQL 5.6 desteklenen: , , *,gbk
*gb2312
cp932
18sjis
.gbk
burada seçeceğiz.Şimdi, çok önemli
SET NAMES
kullanımı burada belirtmek gerekir. Bu karakter kümesiSUNUCUDA. Eğer C aramayı kullansak APImysql_set_charset()
ince (2006-MySQL sürümleri) olurduk işlevi. Ama daha fazla neden bir dakikaYük
Bu enjeksiyon için kullanacağız yükü bayt sırası
0xbf27
ile başlar.gbk
geçersiz boş bir karakter;latin1
29 ** dize.latin1
unutmayınveKendi başınagbk
,0x27
'
edebi bir karakter.Eğer üzerinde
addslashes()
aradık,'
karakter önce Ekle* yani *35 36*, *bir ASCII istiyoruz çünkü bu yükü seçtik.gbk
iki karakter dizisi olduğu0xbf5c27
ile olacağımızı:0xbf5c
0x27
izledi. Ya da başka bir deyişle, birgeçerlikarakter'
çıkmamış izledi. Amaaddslashes()
kullanmıyoruz. Böylece bir sonraki adıma() mysql_real_escape_string
mysql_real_escape_string()
C API ara bağlantı karakter seti bilenaddslashes()
farklıdır. Bu kaçış düzgün sunucu bekleniyor karakter kümesi için gerçekleştirebilirsiniz. Ancak, bu noktaya kadar, müşteri hala biz asla aksini söylediği için bağlantı içinlatin1
kullanarak olduğumuzu düşünüyor. Söyle yaptıkserver* *47, ama kullanıyoruzistemcihalalatin1
olduğunu düşünüyor.Bu nedenle arama
mysql_real_escape_string()
ters eğik çizgi ekler ve ücretsiz bir asma var'
bizim karakter "" içerik! kaçtı Eğergbk
bir karakter kümesi$var
bakacak olursak aslında, görmek istiyoruz:縗' OR 1=1 /*
exactly what saldırı gerektirir.
Sorgu
Bu kısmı sadece formalite, ama burada işlenen sorgu:
SELECT * FROM test WHERE name = '縗' OR 1=1 /*' LIMIT 1
Tebrikler, başarılı bir program 54* *... kullanarak saldırdı
Kötü
Daha da kötüye gidiyor. PDO
varsayılantaklitMySQL ile hazırlanmış deyimleri. İstemci tarafında, temel olarak aşağıdaki başarılı bir enjeksiyon neden olacak demektir mysql_real_escape_string()
(C kütüphanesi), sprintf yok anlamına gelir:
$pdo->query('SET NAMES gbk');
$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$stmt->execute(array("\xbf\x27 OR 1=1 /*"));
Şimdi, özenerek hazırlanmış deyimleri devre dışı bırakarak önleyebilirsiniz bu fazlalaştı:
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
Bugenellikledoğru hazırlanmış bir deyimi (verileri sorgudan ayrı bir paket olarak gönderilen yani sonuç. Ancak, unutmayın PDO olacak sessizce fallback taklit tabloların MySQL olamaz hazırlamak doğal olarak: o olabilir listed manuel, ama dikkat seçmek için uygun sunucu sürümü).
Çirkin
Eğer SET NAMES gbk
yerine mysql_set_charset('gbk')
eskiden olsa bu her önüne geçebilirdik bunu en başında söyledim. Ve 2006 yılından bu yana MySQL sürümü kullanıyorsunuz sağlanan doğru.
Eğer daha önce MySQL sürümü kullanıyorsanız, o zaman mysql_real_escape_string()
89 *Bir kargomuz gibi geçersiz karakterler boş amaçlar kaçmak için tek bayt olarak tedavi edildi anlamına geliyordueğer istemci bağlantı kodlama doğru bilgilendirilmiş olsaydı bileve bu saldırı başarılı olacaktır. Hata MySQL 4.1.20, 5.0.22 92* giderilmiştir.
Ama en kötüsü PDO
5.3.6, yani önceki sürümleri kadar mysql_set_charset()
C API neden yoktuolamazmümkün olan her komut için bu saldırıyı önlemek!
DSN parameter olarak şimdi ortaya.
Saving Grace
Başlangıçta söylediğimiz gibi, bu saldırı çalışması için veritabanı bağlantısı kodlanmış savunmasız bir karakter kümesini kullanarak olmalıdır. utf8mb4
savunmasız değilve henüz destekleyebilirherBunun yerine kullan-tercih edebilirsin;ama sadece MySQL 5.5.3 beri mevcut olmuştur. Unicode karakter: Alternatifi de olan utf8
savunmasız değilve Unicode tüm desteği Basic Multilingual Plane.
Alternatif olarak, mysql_real_escape_string()
çalışmasını değiştiren NO_BACKSLASH_ESCAPES
SQL modu, etkinleştirebilirsiniz. Bu mod, 0x27
etkin 0x2727
ile değiştirilecektir yerine 0x5c27
ve böylece kaçış süreciolamazdaha önce mevcut değildi nerede savunmasız kodlamaları herhangi geçerli bir karakter (yani 0xbf27
hala 0xbf27
vb.)&oluştur- yani sunucu hala geçersiz olarak dize reddeder. Ancak, bu SQL modunu kullanarak ortaya çıkan farklı bir güvenlik açığı için @eggyal's answer.
Güvenli Örnekler
Aşağıdaki örnekler güvenlidir:
mysql_query('SET NAMES utf8');
$var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*");
mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");
Server 74* *... bekliyor çünkü
mysql_set_charset('gbk');
$var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*");
mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");
Düzgün koyduk çünkü karakteri çok istemci ve sunucu maç ayarlayın.
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->query('SET NAMES gbk');
$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$stmt->execute(array("\xbf\x27 OR 1=1 /*"));
Taklit hazırlanan ifadeleri kapattık çünkü.
$pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=gbk', $user, $password);
$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$stmt->execute(array("\xbf\x27 OR 1=1 /*"));
Karakteri düzgün set ettik çünkü.
$mysqli->query('SET NAMES gbk');
$stmt = $mysqli->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$param = "\xbf\x27 OR 1=1 /*";
$stmt->bind_param('s', $param);
$stmt->execute();
Çünkü MySQLi doğru mu hazırlanmış deyimleri her zaman.
Toparlıyor
Eğer:
- MySQL Modern Versiyonları (geç 5.1, 5.5, 5.6, vb) kullanınVE** / *79 80* / bu PDO DSN karakter kümesi parametresi (PHP ≥ 5.3.6)
YA
- Savunmasız bir karakter kullanmayın bağlantı için şifreleme ayarlayın (yalnızca
utf8
/ ** / *82 83* / vb kullanın)
Eğer 0 güvenli.
Aksi takdirde, hassas bir durumdasınmysql_real_escape_string()
kullanıyorsanız bile...
Htmlspecialchars ve mysql_real_escape_...
Nasıl bir scala bağımlılık enjeksiyon ...
Kontrol çevirme vs Bağımlılık Enjeksiy...
Alternatif enjeksiyon piç var mı? (AKA...
Biri bana bu SQL enjeksiyon saldırısı ...