Htmlspecialchars ve mysql_real_escape_string, PHP kodumu enjeksiyondan korur mu?


Yanıtlar:


241

Veritabanı sorguları söz konusu olduğunda, her zaman hazırlanmış parametreli sorguları deneyin ve kullanın. mysqliVe PDOkütüphaneler bu destekler. Bu, gibi kaçan işlevleri kullanmaktan son derece güvenlidir mysql_real_escape_string.

Evet, mysql_real_escape_stringaslında sadece bir dizge kaçan işlevdir. Bu sihirli bir mermi değil. Tek yapacağı, tek bir sorgu dizesinde güvenli bir şekilde kullanılabilmeleri için tehlikeli karakterlerden kaçınmaktır. Ancak, girdilerinizi önceden sterilize etmezseniz, belirli saldırı vektörlerine karşı savunmasız olacaksınız.

Aşağıdaki SQL'i hayal edin:

$result = "SELECT fields FROM table WHERE id = ".mysql_real_escape_string($_POST['id']);

Bunun istismara açık olduğunu görebilmelisiniz. Parametrenin ortak saldırı vektörünü içerdiğini
düşünün id:

1 OR 1=1

Orada kodlanacak riskli karakter yoktur, bu nedenle doğrudan kaçan filtreden geçecektir. Bizi terk ederek:

SELECT fields FROM table WHERE id= 1 OR 1=1

Bu hoş bir SQL enjeksiyon vektörüdür ve saldırganın tüm satırları döndürmesine izin verir. Veya

1 or is_admin=1 order by id limit 1

hangi üretir

SELECT fields FROM table WHERE id=1 or is_admin=1 order by id limit 1

Bu, saldırganın bu tamamen kurgusal örnekte ilk yöneticinin ayrıntılarını geri vermesini sağlar.

Bu işlevler yararlı olsa da, dikkatli kullanılmalıdır. Tüm web girişlerinin bir dereceye kadar doğrulanmasını sağlamanız gerekir. Bu durumda, bir sayı olarak kullandığımız bir değişkenin aslında sayısal olup olmadığını kontrol etmediğimiz için sömürülebileceğimizi görüyoruz. PHP'de, girdilerin tamsayılar, yüzer sayılar, alfasayısal vb. Olduğunu kontrol etmek için yaygın olarak bir dizi işlev kullanmanız gerekir. Ancak, SQL söz konusu olduğunda, hazırlanan ifadenin değerinin çoğuna dikkat edin. Yukarıdaki kod, hazırlanmış bir ifade olsaydı, veritabanı işlevleri bunun 1 OR 1=1geçerli bir literal olmadığını bildiği için güvenli olurdu .

Gelince htmlspecialchars(). Bu kendi başına bir mayın tarlası.

PHP'de gerçek bir sorun var, çünkü html ile ilgili farklı kaçış işlevlerinden oluşan bir seçki var ve tam olarak hangi işlevlerin ne yaptığına dair net bir kılavuz yok.

Öncelikle, bir HTML etiketinin içindeyseniz, gerçekten başınız belada demektir. Bakmak

echo '<img src= "' . htmlspecialchars($_GET['imagesrc']) . '" />';

Zaten bir HTML etiketinin içindeyiz, bu nedenle tehlikeli bir şey yapmamıza <veya> ihtiyacımız yok. Saldırı vektörümüz şöyle olabilir:javascript:alert(document.cookie)

Artık ortaya çıkan HTML şöyle görünüyor:

<img src= "javascript:alert(document.cookie)" />

Saldırı doğrudan geçer.

Daha da kötüleşiyor. Neden? çünkü htmlspecialchars(bu şekilde çağrıldığında) yalnızca çift tırnakları kodlar ve tek değil. Yani olsaydı

echo "<img src= '" . htmlspecialchars($_GET['imagesrc']) . ". />";

Kötü saldırganımız artık tamamen yeni parametreler enjekte edebilir

pic.png' onclick='location.href=xxx' onmouseover='...

bize verir

<img src='pic.png' onclick='location.href=xxx' onmouseover='...' />

Bu durumlarda sihirli bir mermi yoktur, sadece girdiyi kendiniz ölçmeniz gerekir. Kötü karakterleri filtrelemeye çalışırsanız kesinlikle başarısız olursunuz. Beyaz liste yaklaşımını benimseyin ve sadece iyi olan karakterlere izin verin. Vektörlerin ne kadar çeşitli olabileceğine dair örnekler için XSS hile sayfasına bakın

htmlspecialchars($string)HTML etiketlerinin dışında kullansanız bile , çok baytlı karakter kümesi saldırı vektörlerine karşı hala savunmasızsınız.

Olabileceğiniz en etkili yöntem, mb_convert_encoding ve htmlentities kombinasyonunu aşağıdaki gibi kullanmaktır.

$str = mb_convert_encoding($str, 'UTF-8', 'UTF-8');
$str = htmlentities($str, ENT_QUOTES, 'UTF-8');

Bu bile IE6'yı UTF'yi işleme biçimi nedeniyle savunmasız bırakır. Ancak, IE6 kullanımı düşene kadar ISO-8859-1 gibi daha sınırlı bir kodlamaya geri dönebilirsiniz.

Çok baytlı problemlerle ilgili daha derinlemesine bir çalışma için bkz. Https://stackoverflow.com/a/12118602/1820


24
Burada gözden kaçan tek şey, DB sorgusu için ilk örnek ... basit bir intval () enjeksiyonu çözecektir. Bir sayıya ihtiyaç duyduğunuzda dizeye değil mysqlescape ... () yerine her zaman intval () kullanın.
Robert K

11
ve parametreleştirilmiş sorguları kullanmanın verilere her zaman kod olarak değil, veri olarak davranılmasına izin vereceğini unutmayın. PDO gibi bir kitaplık kullanın ve mümkün olduğunda parametreleştirilmiş sorgular kullanın.
Cheekysoft

9
İki açıklama: 1. İlk örnekte, parametrenin etrafına $result = "SELECT fields FROM table WHERE id = '".mysql_real_escape_string($_POST['id'])."'";2. gibi tırnak işaretleri de koyarsanız güvende olursunuz . İkinci durumda (URL içeren özellik), bunun hiçbir faydası yoktur htmlspecialchars; bu durumlarda, girişi bir URL kodlama şeması kullanarak kodlamalısınız, örneğin rawurlencode. Bu şekilde, kullanıcı vb javascript:. Ekleyemez.
Marcel Korpel

7
"Htmlspecialchars yalnızca çift tırnakları kodlar, tek değil": bu doğru değildir, ayarlanan bayraklara bağlıdır, parametrelerine bakın .
Marcel Korpel

2
Bu kalın yazılmalıdır: Take a whitelist approach and only let through the chars which are good.Kara liste her zaman bir şeyleri gözden kaçırır. +1
Jo Smo

10

Cheekysoft'un mükemmel cevabına ek olarak:

  • Evet, sizi güvende tutacaklardır, ancak yalnızca kesinlikle doğru kullanılırlarsa. Bunları yanlış kullanırsanız savunmasız kalırsınız ve başka sorunlar yaşayabilirsiniz (örneğin veri bozulması)
  • Lütfen bunun yerine parametreli sorgular kullanın (yukarıda belirtildiği gibi). Bunları örneğin PDO veya PEAR DB gibi bir paketleyici aracılığıyla kullanabilirsiniz.
  • Magic_quotes_gpc ve magic_quotes_runtime'ın her zaman kapalı olduğundan ve kısa süreliğine bile olsa kazara açılmadığından emin olun. Bunlar, PHP geliştiricilerinin güvenlik sorunlarını (verileri yok eden) önlemeye yönelik erken ve derinlemesine yanlış yönlendirilmiş bir girişimidir.

HTML enjeksiyonunu (örneğin, siteler arası komut dosyası oluşturma) önlemek için gerçekten sihirli bir değnek yoktur, ancak HTML çıktısını almak için bir kitaplık veya şablonlama sistemi kullanıyorsanız, bunu daha kolay başarabilirsiniz. Eşyalardan uygun şekilde nasıl kaçılacağını öğrenmek için bunun belgelerini okuyun.

HTML'de, içeriğe bağlı olarak şeylerden farklı şekilde kaçınılması gerekir. Bu özellikle Javascript'e yerleştirilen dizeler için geçerlidir.


3

Yukarıdaki gönderilere kesinlikle katılıyorum, ancak Cheekysoft'un cevabına yanıt olarak eklemem gereken küçük bir şey var, özellikle:

Veritabanı sorguları söz konusu olduğunda, her zaman hazırlanmış parametreli sorguları deneyin ve kullanın. Mysqli ve PDO kitaplıkları bunu destekler. Bu, mysql_real_escape_string gibi kaçan işlevler kullanmaktan son derece güvenlidir.

Evet, mysql_real_escape_string etkili bir şekilde sadece bir dizge kaçan işlevdir. Bu sihirli bir mermi değil. Tek yapacağı, tek bir sorgu dizesinde güvenli bir şekilde kullanılabilmeleri için tehlikeli karakterlerden kaçınmaktır. Ancak, girdilerinizi önceden sterilize etmezseniz, belirli saldırı vektörlerine karşı savunmasız olacaksınız.

Aşağıdaki SQL'i hayal edin:

$ sonuç = "WHERE tablodan alanları SEÇİN id =" .mysql_real_escape_string ($ _ POST ['id']);

Bunun istismara açık olduğunu görebilmelisiniz. İd parametresinin ortak saldırı vektörünü içerdiğini düşünün:

1 VEYA 1 = 1

Orada kodlanacak riskli karakter yoktur, bu nedenle doğrudan kaçan filtreden geçecektir. Bizi terk ederek:

İd = 1 VEYA 1 = 1 NEREDE tablodan alanları seçin

Veri tabanı sınıfıma koyduğum ve sayı olmayan herhangi bir şeyi çıkaracak hızlı küçük bir işlevi kodladım. Preg_replace kullanır, bu nedenle prob biraz daha optimize edilmiş işlev vardır, ancak bir tutam içinde çalışır ...

function Numbers($input) {
  $input = preg_replace("/[^0-9]/","", $input);
  if($input == '') $input = 0;
  return $input;
}

Yani kullanmak yerine

$ sonuç = "WHERE tablodan alanları SEÇİN id =" .mysqlrealescapestring ("1 VEYA 1 = 1");

Kullanmak istiyorum

$ sonuç = "WHERE tablodan alanları SEÇİN id =". Sayılar ("1 VEYA 1 = 1");

ve sorguyu güvenle çalıştırır

NEREDE id = 111 tablodan alanları seçin

Elbette, bu sadece doğru satırı görüntülemesini engelledi, ancak sitenize sql enjekte etmeye çalışan herkes için bunun büyük bir sorun olduğunu sanmıyorum;)


1
Mükemmel! Bu tam da ihtiyacınız olan temizlik türüdür. Bir sayının sayısal olduğunu doğrulamadığından ilk kod başarısız oldu. Kodunuz bunu yapar. Değerleri kod tabanı dışından gelen tüm tamsayı kullanım değişkenlerinde Numbers () çağırmalısınız.
Cheekysoft

1
PHP tam sayıları sizin için otomatik olarak dizelere zorladığından, intval () işlevinin bunun için mükemmel bir şekilde çalışacağını belirtmekte fayda var.
Adam Ernst

11
Ben intvali tercih ederim. 12 değil 1abc2'yi 1'e çeviriyor.
jmucchiello

1
intval, özellikle ID'de daha iyidir. Çoğu zaman, eğer bozulmuşsa, aynen yukarıdaki gibidir, 1 veya 1 = 1. Gerçekten başkalarının kimliğini sızdırmamalısın. Dolayısıyla intval, doğru kimliği döndürecektir. Bundan sonra, orijinal ve temizlenmiş değerlerin aynı olup olmadığını kontrol etmelisiniz. Bu, yalnızca saldırıları durdurmanın değil, saldırganları bulmanın harika bir yoludur.
2014

2
Kişisel verileri gösterirseniz yanlış satır felaket olur, başka bir kullanıcının bilgilerini görürsünüz! bunun yerine kontrol etmek daha iyi olurreturn preg_match('/^[0-9]+$/',$input) ? $input : 0;
Frank Forte

2

Bu bulmacanın önemli bir parçası bağlamlardır. Sorgunuzdaki her bağımsız değişkenden alıntı yaparsanız, birisinin kimlik olarak "1 VEYA 1 = 1" göndermesi sorun olmaz:

SELECT fields FROM table WHERE id='".mysql_real_escape_string($_GET['id'])."'"

Hangi sonuç:

SELECT fields FROM table WHERE id='1 OR 1=1'

ki bu etkisizdir. Dizeden kaçtığınız için, girdi dize bağlamından kopamaz. Bunu MySQL'in 5.0.45 sürümüne kadar test ettim ve bir tamsayı sütunu için bir dize bağlamı kullanmak herhangi bir soruna neden olmaz.


15
ve sonra saldırı vektörüme, latin1 veritabanınızda filtre işlevi tarafından 0xbf5c27 olarak dönüştürülecek olan çok baytlı karakter 0xbf27 ile başlayacağım - bu tek bir çok baytlı karakter ve ardından tek bir alıntı.
Cheekysoft

8
Bilinen tek bir saldırı vektörüne karşı koruma sağlamamaya çalışın. Kodunuza yamadan sonra yama uygulayarak zamanın sonuna kadar kuyruğunuzu kovalamaya başlayacaksınız. Geride durmak ve genel durumlara bakmak, daha güvenli koda ve daha iyi güvenlik odaklı bir zihniyete yönelecektir.
Cheekysoft

Katılıyorum; İdeal olarak, OP hazırlanmış ifadeleri kullanacaktır.
Lucas Umman

1
Bu gönderi tarafından önerilen argümanların alıntılanması kusursuz olmasa da, yaygın 1 VEYA 1 = 1 tipi saldırıların çoğunu hafifletecek, bu yüzden bahsetmeye değer.
Gece Baykuşu

2
$result = "SELECT fields FROM table WHERE id = ".(INT) $_GET['id'];

64 bit sistemlerde daha da iyi çalışır. Yine de büyük sayıları adreslemede sistem sınırlamalarınıza dikkat edin, ancak veritabanı kimlikleri için bu, çoğu zaman harika çalışır.

Değerlerinizi temizlemek için de tek bir işlev / yöntem kullanmalısınız. Bu işlev sadece mysql_real_escape_string () için bir sarmalayıcı olsa bile. Neden? Çünkü bir gün, tercih ettiğiniz temizleme verisi yönteminden yararlanma bulunduğunda, sistem genelinde bir bul ve değiştir yerine, onu yalnızca bir yerde güncellemeniz gerekir.


-3

neden, oh neden olmasın sizin sql deyiminde kullanıcı girişi tırnak içine? yapmamak oldukça aptalca görünüyor! sql ifadenize tırnak işareti eklemek "1 veya 1 = 1" denemesi sonuçsuz kalır, değil mi?

bu yüzden şimdi, "kullanıcı girdiye bir alıntı (veya çift tırnak) eklerse ne olur?" diyeceksiniz.

bunun için kolay düzeltme: kullanıcı tarafından girilen alıntıları kaldırmanız yeterlidir. örneğin: input =~ s/'//g;. şimdi, bana öyle geliyor ki, kullanıcı girişi güvenli olacaktı ...


"neden, oh NEDEN, sql deyiminize kullanıcı girdilerinin etrafına alıntılar eklemeyesiniz?" - Soru, kullanıcı girdisinden alıntı yapmamak hakkında hiçbir şey söylemiyor.
Quentin

1
"iyi, bunun için kolay düzeltme" - Bunun için korkunç bir düzeltme. Bu, verileri çöpe atar. Soruda bahsedilen çözüm daha iyi bir yaklaşımdır.
Quentin

Sorunun kullanıcı girdisini alıntılamayı ele almadığını kabul etsem de, yine de girdiyi alıntı yapmamak gibi görünüyor. ve hatalı veri girmek yerine verileri atmayı tercih ederim. genel olarak, bir enjeksiyon saldırısında, bu verileri zaten İSTEMİYORSUNUZ .... değil mi?
Jarett L

"Sorunun, kullanıcı girdisini alıntılamayı ele almadığını kabul etsem de, yine de girdiden alıntı yapmamak gibi görünüyor." - Hayır değil. Soru onu öyle ya da böyle göstermiyor.
Quentin

1
@JarettL Ya hazırlanmış ifadeleri kullanmaya ya da Bobby Tables'ın her Salı verilerinizi mahvetmesine alışın . Parametreli SQL, SQL enjeksiyonuna karşı kendinizi korumanın tek en iyi yoludur. Hazırlanmış bir ifade kullanıyorsanız "SQL enjeksiyon kontrolleri" yapmanız gerekmez. Uygulanması son derece kolaydır (ve bence kodu okumayı ÇOK daha kolay hale getirir), dizi birleştirme ve sql enjeksiyonunun çeşitli özelliklerinden korurlar ve en iyisi, onu uygulamak için tekerleği yeniden icat etmeniz gerekmez. .
Siyual
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.