SORU
21 NİSAN 2011, PERŞEMBE


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
25 AĞUSTOS 2012, CUMARTESİ


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:

  1. 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 *gb2312cp93218 sjis. gbk burada seçeceğiz.

    Şimdi, çok önemli SET NAMES kullanımı burada belirtmek gerekir. Bu karakter kümesiSUNUCUDA. Eğer C aramayı kullansak API mysql_set_charset() ince (2006-MySQL sürümleri) olurduk işlevi. Ama daha fazla neden bir dakika

  2. Yü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şına gbk, 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ğu 0xbf5c27 ile olacağımızı: 0xbf5c 0x27 izledi. Ya da başka bir deyişle, birgeçerlikarakter ' çıkmamış izledi. Ama addslashes() kullanmıyoruz. Böylece bir sonraki adıma

  3. () mysql_real_escape_string

    mysql_real_escape_string() C API ara bağlantı karakter seti bilen addslashes() 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çin latin1 kullanarak olduğumuzu düşünüyor. Söyle yaptıkserver* *47, ama kullanıyoruzistemcihala latin1 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ğer gbk bir karakter kümesi $var bakacak olursak aslında, görmek istiyoruz:

    縗' OR 1=1 /*

    exactly what saldırı gerektirir.

  4. 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. utf8mb4savunması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 utf8savunması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ızcautf8 / ** / *82 83* / vb kullanın)

Eğer 0 güvenli.

Aksi takdirde, hassas bir durumdasınmysql_real_escape_string() kullanıyorsanız bile...

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Adam Outler

    Adam Outler

    19 EKİM 2006
  • CrazyMan

    CrazyMan

    14 Mayıs 2008
  • tsweeney79

    tsweeney79

    21 Ocak 2008