Açılır menü kullanırsam SQL enjeksiyonuna karşı korunmam gerekir mi?


97

Temelde SQL enjeksiyonu olasılığı nedeniyle, bir formdan gelen kullanıcı girişine ASLA güvenmemeniz gerektiğini anlıyorum.

Bununla birlikte, bu aynı zamanda tek girişin bir açılır menüden (aşağıya bakın) olduğu bir form için de geçerli midir?

Bunu $_POST['size']daha sonra site genelinde çeşitli veritabanlarını sorgulamak için (bir mysqliSeçim sorgusuyla) kullanılan bir Oturuma kaydediyorum ve herhangi bir SQL enjeksiyonu kesinlikle onlara zarar verir (muhtemelen düşürür).

Veritabanlarını sorgulamak için yazılan kullanıcı girişi alanı yoktur, sadece açılır menüler (ler).

<form action="welcome.php" method="post">
<select name="size">
  <option value="All">Select Size</option> 
  <option value="Large">Large</option>
  <option value="Medium">Medium</option>
  <option value="Small">Small</option>
</select>
<input type="submit">
</form>

112
Evet. Hiçbir şey bir saldırganın sizin <select>girişinizde dilediği değerleri göndermesini engellemez . Aslında biraz teknik bir kullanıcı bile tarayıcı konsolunu kullanarak ek seçenekler ekleyebilir. mevcut değerlerin bir dizi beyaz listesini tutar ve bununla girdiyi karşılaştırırsanız, bunu azaltabilirsiniz (ve istenmeyen değerleri engellediği için yapmanız gerekir)
Michael Berkowski

13
Temel istek / yanıt konularını ve ön ucun istek üzerine nasıl oluşturulduğunun önemi olmayan şeyi anlamalısınız, yani bu durumda açılır menü
Royal Bg

13
@YourCommonSense Çünkü bu iyi bir soru. Bir müşterinin ne kadar manipüle edilebilir olduğunun herkes farkında değildir. Bu, bu siteye çok değerli cevaplar getirecektir.
Cruncher

8
@Cruncher görüyorum. Ortalama bir yığın taşan için, daha önce duydukları bir roket bilimidir. PHP etiketi altında en çok sorulan soruya rağmen.
Sizin Common Sense

9
"Kullanıcı girdisine ASLA güvenmemeniz gerektiğini anlıyorum". İstisna yok.
Prinzhorn

Yanıtlar:


69

Gönderilen boyutun beklediğiniz gibi olduğundan emin olmak için aşağıdaki örnek kadar basit bir şey yapabilirsiniz.

$possibleOptions = array('All', 'Large', 'Medium', 'Small');

if(in_array($_POST['size'], $possibleOptions)) {
    // Expected
} else {
    // Not Expected
}

Ardından, sonucunuzu kaydetmek için olması gereken php> = 5.3.0 sürümünü kullanıyorsanız mysqli_ * kullanın. Doğru kullanılırsa bu, sql enjeksiyonuna yardımcı olacaktır.


Bu, "beyaz liste" OliverBS'in kısa versiyonu mu?
Tatters

Sadece birinin çok basit bir sürümü değil, değerleri daha kolay ve yeniden kullanılabilir hale getirmek için kontrol etmek üzere bir veritabanına ekleyebilir veya her beyaz liste için kontrol edilecek belirli bir yöntem içeren bir beyaz liste sınıfı oluşturabilirsiniz. Bir veritabanı kullanmak istemediyseniz, beyaz liste özellikleri, beyaz liste sınıfınızdaki bir dizi özelliği içinde olabilir.
Oliver Bayes-Shelton

9
Yine de Hazırlanmış İfadeleri (önerilen) veya kullanmalısınız mysqli_real_escape_string. Ayrıca, bunların çıktısını alırken değerlerden uygun şekilde kaçış yapın (örneğin , bir HTML belgesinde htmlspecialchars () kullanın ).
ComFreek

4
Ben üçüncü parametre ayarı öneririm in_arrayiçin truesıkı karşılaştırma için. Neyin ters gidebileceğinden emin değilim, ancak gevşek karşılaştırma oldukça tuhaf.
Brilliand

1
@OliverBS $ _POST değerleri diziler de olabilir. Sayısal dizeler sayılarla karşılaştırılır ('5' == '05'). Örneğinizde bir güvenlik açığı olduğunu sanmıyorum, ancak kurallar karmaşık ve anlamadığım nedenlerle delikler açılabilir. Kesin karşılaştırma yapmak daha kolaydır ve bu nedenle güvenli bir şekilde kullanmak daha kolaydır.
Brilliand

195

Evet buna karşı korunmanız gerekiyor.

Firefox'un geliştirici konsolunu kullanarak nedenini size göstereyim:

açılır menüdeki değerlerden birini drop table ifadesi olacak şekilde düzenledim

Bu verileri temizlemezseniz, veritabanınız imha edilecektir. (Bu tamamen geçerli bir SQL ifadesi olmayabilir, ancak umarım amacımı anlamışımdır.)

Açılır listenizde mevcut olan seçenekleri sınırlamış olmanız, sunucunuza gönderebileceğim verileri sınırlandırdığınız anlamına gelmez .

Sayfanızdaki davranışı daha fazla kısıtlamaya çalıştıysanız, seçeneklerim arasında bu davranışı devre dışı bırakmak veya sunucunuza bu form gönderimini taklit eden özel bir HTTP isteği yazmak yer alır. Orada adında bir araçtır bukle tam olarak bu için kullanılır ve ben düşünmek komut böyle bir şey olmazdı zaten bu SQL enjeksiyonu göndermek için:

curl --data "size=%27%29%3B%20DROP%20TABLE%20*%3B%20--"  http://www.example.com/profile/save

(Bu tamamen geçerli bir curl komutu olmayabilir, ancak yine, umarım amacımı anlamışımdır.)

O yüzden şunu tekrar edeceğim:

ASLA kullanıcı girdisine güvenmeyin. DAİMA kendinizi koruyun.

Herhangi bir kullanıcı girdisinin güvenli olduğunu varsaymayın. Form dışında başka yollarla gelse bile potansiyel olarak güvensizdir. Hiçbiri kendinizi SQL enjeksiyonundan korumayı bırakacak kadar güvenilir değildir.


24
İle özel bir yük oluşturmaktan bahsetmiyorum bile curl. DAİMA giriş sunucusu tarafını sterilize edin !
recursion.ninja

14
Bir görüntü binden fazla kelimeyi anlatır.
davidkonrad

4
Varsayılan cevap bu olmalıdır. Ayrıca hakkında bir şey içermelidir curl. İnsanlar, herhangi bir format kullanarak herhangi bir yerden herhangi bir yere bir HTTP isteği gönderebileceğinizi ve herhangi bir değeri iletebileceğinizi anlamıyor, isteğin işlemeden önce geçerli olduğundan emin olmak sunucuya bağlıdır.
retrohacker

2
Ne kadar tatlı! Bu küçük Bobby Masalar!
Aura

45

Bu soru etiketlendiğinden , işte bu tür saldırıya ilişkin bir cevap:

Yorumlarda söylendiği gibi, istisnasız herhangi bir değişken veriyi içeren her bir sorgu için hazırlanmış ifadeler kullanmanız gerekir .

Herhangi bir HTML malzemesinden bağımsız olarak! HTML girişi veya başka herhangi bir dış faktörden bağımsız
olarak SQL sorgularının uygun şekilde biçimlendirilmesi gerektiğini anlamak önemlidir .

Giriş doğrulama amacıyla diğer yanıtlarda önerilen beyaz listeyi kullanabilmenize rağmen, SQL ile ilgili herhangi bir eylemi etkilememelidir - HTML girişini doğrulasanız da doğrulamasanız da bunlar aynı kalmalıdır. Bu, sorguya herhangi bir değişken eklerken yine de hazırlanmış ifadeleri kullanmanız gerektiği anlamına gelir.

Burada ayrıntılı bir açıklama, hazırlanmış ifadelerin neden bir zorunluluk olduğu ve bunların nasıl doğru bir şekilde kullanılacağı ve nerede uygulanamayacağı ve bu durumda ne yapılması gerektiği hakkında kapsamlı bir açıklama bulabilirsiniz: Otostopçunun SQL Enjeksiyon Koruması Kılavuzu

Ayrıca bu soru şu şekilde etiketlendi: . Çoğunlukla tesadüfen sanırım, ama yine de ham mysqli'nin eski mysq_ * işlevlerinin yerine geçemeyeceği konusunda sizi uyarmalıyım . Basitçe, çünkü eski tarzda kullanılırsa hiçbir güvenlik sağlamayacaktır. Hazırlanan ifadeleri desteklemek acı verici ve zahmetli olsa da, ortalama bir PHP kullanıcısı bunları hiçbir şekilde çalıştıramaz. Bu nedenle, ORM veya bir tür soyutlama kitaplığı seçeneği yoksa , PDO tek seçeneğinizdir.


5
i = rastgele (0, 15); // i kullanarak bir sorgu. Yine de burada açıklama hazırlamam gerekiyor mu?
Cruncher

2
Uygulaması nedir random?
Dilimli tavası

9
@YourCommonSense dar görüntülendi mi? Bana göre "Her zaman X yapın ve bunun için hiçbir sebep sunmayacağım" dar görüşlüdür.
Cruncher

5
@Cruncher ben herhangi bir nedenle düşünemiyorum için olamaz , her zaman , her zaman, her şey için hazır deyimleri, her zaman kullanın. Bana bir tane söyleyebilir misin?
Wesley Murch

3
@Cruncher Aynı fikirdeyim, sanki Devil's Advocate oynuyor gibisin. Yanıttaki şart aynı zamanda "herhangi bir değişken veriyi de içeriyor" . PHP int casting gibi birkaç durum olabilir ama sadece hazırlanmış deyimleri kullanmak daha iyi görünüyor. (Deneyimsiz) insanlara küçük bir performans "artışının" güvenlik kapalı olduğundan daha önemli olduğunu söylemek. Cevap "eksik" ama diğerlerinin görmezden geldiği güçlü bir mesaj veriyor. Davamı dinlendireceğim.
Wesley Murch

12

Evet.

Herkes gerçekten gönderilen değerler için her şeyi taklit edebilir -

Öyleyse, açılır menüleri doğrulamak için, üzerinde çalıştığınız değerin açılır menüde olup olmadığını kontrol edebilirsiniz - bunun gibi bir şey en iyi (en mantıklı paranoyak) yol olacaktır:

if(in_array($_POST['ddMenu'], $dropDownValues){

    $valueYouUseLaterInPDO = $dropDownValues[array_search("two", $arr)];

} else {

    die("effin h4x0rs! Keep off my LAMP!!"); 

}

5
Bu cevap eksik, ayrıca hazır ifadeler kullanmalısınız, böylece değerin ne olduğuna bakılmaksızın enjeksiyon mümkün olmaz
Dilimleme

7
@Slicedpan Gerçekten mi? Bu, nedenini bilmeden çoğunluğa atlıyormuşsunuz gibi geliyor ... Bir sorguya bir avuç dolusu olası girdim varsa, bunların hepsinin iyi olduğunu doğrulayabilirim (ki biliyorum, çünkü onları ben yaptım), hazır bir ifadeyi kullanarak hiçbir ek güvenlik avantajı elde
Cruncher

3
Bir kural olarak hazırlanmış ifadelerin kullanılmasını zorunlu kılmak sizi gelecekte SQL enjeksiyon güvenlik açıklarına sokmaktan korur.
Slicedpan

1
@Cruncher "Açılır menüdeki tüm değerler her zaman güvende olacak" sözünü vermek çok güvenli olmayan bir sözdür. Değerler değiştirilir, kod mutlaka güncellenmez. Değerleri güncelleyen kişi, güvenli olmayan değerin ne olduğunu bile bilmeyebilir! Özellikle her türlü güvenlik endişesiyle dolu web programlama alanında, böyle bir şeye göz atmak tamamen sorumsuzluktur (ve daha az hoş sözler).
hyde

1
@Cruncher SQL işlerinizi düzgün bir şekilde hallederseniz, SQL'in düzgün çalışmasına müdahale etmeden herhangi bir girişi kabul edebilirsiniz . SQL bölümü hazırlanmalı ve nereden gelirse gelsin her şeyi kabul etmeye hazır olmalıdır. Diğer her şey hataya meyillidir.
glglgl

8

Konsolu kullanarak açılır listenizi değiştiren kullanıcılara karşı korumanın bir yolu, içlerinde yalnızca tamsayı değerleri kullanmaktır. Ardından POST değerinin bir tamsayı içerdiğini doğrulayabilir ve gerektiğinde bunu metne dönüştürmek için bir dizi kullanabilirsiniz. Örneğin:

<?php
// No, you don't need to specify the numbers in the array but as we're using them I always find having them visually there helpful.
$sizes = array(0 => 'All', 1 => 'Large', 2 => 'Medium', 3 => 'Small');
$size = filter_input(INPUT_POST, "size", FILTER_VALIDATE_INT);

echo '<select name="size">';
foreach($sizes as $i => $s) {
    echo '<option value="' . $i . '"' . ($i == $size ? ' selected' : '') . '>' . $s . '</option>';
}
echo '</select>';

Daha sonra $sizesorgunuzda yalnızca FALSEbir tam sayı içereceğini bilerek kullanabilirsiniz .


2
@OliverBS filter_inputSunucu tarafında kontrol değilse ne olur ? Tam sayı dışında hiç kimse bir şey gönderemez.
Styphon

Teşekkürler Styphon, O halde bu OP'deki formun yerini alıyor mu? Ve bu, birden çok açılır menüye sahip daha büyük formlar için geçerli olur mu? 'Renk' demek için başka bir açılır menü eklersem?
Tatters

@SamuelTattersfield Evet ve evet. Bunu, istediğiniz kadar seçenekle, istediğiniz kadar açılır menü için kullanabilirsiniz. Her açılır menü için yeni bir dizi oluşturursunuz ve dizideki açılır menü için tüm seçenekleri koyarsınız.
Styphon

Güzel! Bunu sevdim. Bir şans vereceğim ve testte ne kazandığımı göreceğim.
Tatters

@SamuelTattersfield Harika, sadece filter_inputparçayı doğrulama için de kullandığınızdan emin olun , aksi takdirde güvenlik için bir çikolata çaydanlık kadar kullanışlıdır.
Styphon

7

Diğer cevaplar zaten bilmeniz gerekenleri kapsıyor. Ama belki biraz daha netleştirmeye yardımcı olur:

Orada İKİ ŞEY yapmanız gereken:

1. Form verilerini doğrulayın.

As Jonathan Hobbs'un cevabı gösterileri çok net form girişi için html elemanı seçimi sizin için herhangi güvenilir filtreleme yapmaz.

Doğrulama genellikle verileri değiştirmeyecek, ancak "Lütfen bunu düzeltin" olarak işaretlenmiş alanlarla formu tekrar gösterecek şekilde yapılır.

Çoğu çerçeve ve CMS'de bu görevde size yardımcı olan form oluşturucular bulunur. Ve sadece bu değil, aynı zamanda başka bir saldırı türü olan CSRF'ye (veya "XSRF") karşı da yardımcı olurlar.

2. SQL ifadelerindeki değişkenleri temizle / kaldır ..

.. veya hazırlanmış ifadelerin sizin için işi yapmasına izin verin.

Kullanıcı tarafından sağlanan veya sağlanmayan herhangi bir değişkenle bir (My) SQL ifadesi oluşturursanız, bu değişkenlerden çıkış yapmanız ve bu değişkenlerden alıntı yapmanız gerekir.

Genel olarak, bir MySQL ifadesine eklediğiniz bu tür herhangi bir değişken ya bir dizge ya da PHP'nin MySQL'in sindirebileceği bir dizeye güvenilir bir şekilde dönüştürülebileceği bir şey olmalıdır. Sayılar gibi.

Dizeler için, dizeden kaçmak için birkaç yöntemden birini seçmeniz gerekir, yani MySQL'de yan etkileri olabilecek karakterleri değiştirin.

  • Eski usul MySQL + PHP'de, mysql_real_escape_string () işi yapar. Sorun, unutmanın çok kolay olmasıdır, bu nedenle kesinlikle hazırlanmış ifadeler veya sorgu oluşturucular kullanmalısınız.
  • MySQLi'de hazırlanmış ifadeleri kullanabilirsiniz.
  • Çoğu çerçeve ve CMS, bu görevde size yardımcı olan sorgu oluşturucular sağlar.

Bir sayı ile uğraşıyorsanız, kaçışları ve tırnak işaretlerini atlayabilirsiniz (bu nedenle hazırlanan ifadeler bir tür belirtmeye izin verir).

SQL ifadesi için değişkenlerden kaçtığınızı ve veritabanının kendisi için DEĞİLDİĞİNİZİ belirtmek önemlidir . Veritabanı orijinal dizeyi saklayacaktır, ancak ifadenin öncelenmiş bir sürüme ihtiyacı vardır.

Bunlardan birini atlarsanız ne olur?

Form doğrulamasını kullanmıyorsanız , ancak SQL girişinizi temizliyorsanız, her türlü kötü şey olduğunu görebilirsiniz, ancak SQL enjeksiyonunu görmezsiniz! (*)

İlk olarak, başvurunuzu planlamadığınız bir duruma alabilir. Örneğin, tüm kullanıcıların ortalama yaşını hesaplamak istiyorsanız, ancak bir kullanıcı yaş için "aljkdfaqer" verdiyse, hesaplamanız başarısız olacaktır.

İkinci olarak, göz önünde bulundurmanız gereken her türlü başka enjeksiyon saldırısı olabilir: Örneğin, kullanıcı girişi javascript veya başka şeyler içerebilir.

Veritabanında hala sorunlar olabilir: Örneğin, bir alan (veritabanı tablosu sütunu) 255 karakterle sınırlıysa ve dize bundan daha uzunsa. Veya alan yalnızca sayıları kabul ediyorsa ve bunun yerine sayısal olmayan bir dize kaydetmeye çalışıyorsanız. Ama bu "enjeksiyon" değil, sadece "uygulamanın çökmesi".

Ancak, hiçbir doğrulama olmadan herhangi bir girişe izin verdiğiniz serbest bir metin alanınız olsa bile, bir veritabanı ifadesine gittiğinde doğru şekilde çıkış yaparsanız, bunu yine de veritabanına kaydedebilirsiniz. Sorun, bu dizeyi bir yerde kullanmak istediğinizde ortaya çıkar.

(*) yoksa bu gerçekten egzotik bir şey olurdu.

SQL ifadeleri için değişkenlerden çıkış yapmazsanız , ancak form girişini doğruladıysanız, yine de kötü şeyler olduğunu görebilirsiniz.

İlk olarak, verileri veritabanına kaydettiğinizde ve tekrar yüklediğinizde, artık aynı veriler olmayacak, "çeviride kaybolacak" riskiyle karşı karşıyasınız.

İkinci olarak, geçersiz SQL ifadelerine neden olabilir ve bu nedenle uygulamanızı çökertebilir. Örneğin, herhangi bir değişken bir tırnak veya çift tırnak karakteri içeriyorsa, kullandığınız alıntı türüne bağlı olarak geçersiz MySQL ifadesi alırsınız.

Üçüncüsü, yine de SQL enjeksiyonuna neden olabilir.

Formlardan gelen kullanıcı girdiniz zaten filtrelenmiş / doğrulanmışsa, kasıtlı SQl yerleştirme olasılığı daha az olabilir, EĞER girişiniz sabit kodlu bir seçenekler listesine indirgenirse veya sayılarla sınırlıysa. Ancak, SQL ifadelerindeki değişkenlerden doğru şekilde çıkış yapmazsanız, herhangi bir serbest metin girişi SQL enjeksiyonu için kullanılabilir.

Ve hiç bir form girdiniz olmasa bile, her tür kaynaktan dizeleriniz olabilir: Dosya sisteminden okuyun, internetten kazınmış vs. Hiç kimse bu dizelerin güvenli olduğunu garanti edemez.


Ne çok uzun veri sayısal alana bir dize şey çökecek değil
Sizin Common Sense

hmm, bunu şimdi denedim ve aslında bir hata yerine bir uyarı gösteriyor. Sanırım bunu bir hataya çeviren PDO. Eminim drupal.org'da bazı dizelerin bir varchar için çok uzun olduğu bir düzine konuyu tartıştım.
donquixote

6

Web tarayıcınız php'den bir sayfa aldığını "bilmiyor", tek gördüğü html. Ve http katmanı bundan daha azını biliyor. Http katmanını geçebilecek hemen hemen her tür girdiyi idare edebilmeniz gerekir (neyse ki çoğu girdi php zaten bir hata verecektir). Kötü niyetli isteklerin veritabanınızı bozmasını önlemeye çalışıyorsanız, diğer taraftaki kişinin ne yaptığını bildiğini ve normal koşullar altında tarayıcınızda görebileceklerinizle sınırlı olmadığını varsaymanız gerekir ( bir tarayıcının geliştirici araçlarıyla neler yapabileceğinizden bahsetmeye gerek yok). Yani evet, açılır listenizdeki herhangi bir girdiyi karşılamanız gerekir, ancak çoğu girdi için bir hata verebilirsiniz.


6

Kullanıcıyı yalnızca belirli bir açılır listeden değerleri kullanmakla sınırlamış olmanız konu dışıdır. Teknik bir kullanıcı, sunucunuza gönderilen http talebini ağlarından ayrılmadan önce yakalayabilir, yerel proxy sunucusu gibi bir araç kullanarak değiştirebilir ve yoluna devam edebilir. Değiştirilen isteği kullanarak, açılır listede belirttiğiniz olmayan parametre değerlerini gönderebilirler. Bir müşterideki herhangi bir şey değiştirilebileceğinden, geliştiriciler, müşteri kısıtlamalarının genellikle anlamsız olduğu zihniyetine sahip olmalıdır. İstemci verilerinin girdiği her noktada sunucu doğrulaması gerekir . Saldırganlar, geliştiricilerin bu tek yönüyle saflığına güveniyor.


5

SQL enjeksiyonuna karşı emin olmak için parametreli bir sorgu kullanmak en iyisidir. Bu durumda sorgunun görünümü şu olacaktır:

SELECT * FROM table WHERE size = ?

Bütünlük için doğrulanmamış metin içeren (giriş sunucuda doğrulanmaz) ve SQL enjeksiyon kodunu içeren yukarıdaki gibi bir sorgu sağladığınızda doğru şekilde işlenecektir. Diğer bir deyişle, istek, veritabanı katmanında buna benzer bir şeyle sonuçlanacaktır:

SELECT * FROM table WHERE size = 'DROP table;'

Bu, yalnızca 0 sonucu seçecektir ve bu da sorguyu bir beyaz listeye, doğrulama denetimine veya diğer tekniklere gerek kalmadan veritabanına gerçekten zarar vermede etkisiz hale getirecektir. Sorumlu bir programcının katmanlar halinde güvenliği sağlayacağını ve sorguları parametrelendirmeye ek olarak genellikle doğrulayacağını lütfen unutmayın. Bununla birlikte, sorgularınızı performans açısından parametrelendirmemek için çok az neden vardır ve bu uygulamanın sağladığı güvenlik, kendinizi parametreli sorgulara alıştırmak için iyi bir nedendir.


4

Formunuzdan gönderilen her ne varsa, sunucunuza kablolar üzerinden metin olarak gelir. Kimsenin istemciyi taklit etmek için bir bot oluşturmasını veya isterlerse bir terminalden yazmasını engelleyen hiçbir şey yoktur. Asla istemciyi programladığınız için düşündüğünüz gibi davranacağını varsaymayın. Bu sahteciliği gerçekten çok kolay.

Müşteriye güvendiğinizde neler olabileceğine ve olacağına dair bir örnek .


4

Bir bilgisayar korsanı, Telnet kullanarak bir istek göndererek tarayıcıyı Javascript form kontrolü dahil olmak üzere tamamen atlayabilir. Tabii ki, kullanmak zorunda olduğu alan adlarını almak için html sayfanızın koduna bakacaktır, ancak o andan itibaren onun için 'her şey gider'. Bu nedenle, sunucuda gönderilen tüm değerleri html sayfanızdan gelmiyormuş gibi kontrol etmelisiniz.

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.