28 Soru: PHP'de SQL enjeksiyonunu nasıl önleyebilirim?

tarafından oluşturulan soru Sat, Oct 1, 2016 12:00 AM

Kullanıcı girişi, bir SQL sorgusuna değişiklik yapılmadan eklenirse, uygulama, aşağıdaki gibi olduğu gibi, SQL Injection 'a karşı savunmasız kalır aşağıdaki örnek:

 
$unsafe_variable = $_POST['user_input']; 

mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe_variable')");

Bunun nedeni, kullanıcının value'); DROP TABLE table;-- gibi bir şey girmesi ve sorgunun gerçekleşmesidir:

 
INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')

Bunun olmasını önlemek için ne yapılabilir?

    
2776
28 Yanıtlar                              28                         

Hazırlanan ifadeleri ve parametreli hale getirilmiş sorguları kullanın. Bunlar, veritabanı sunucusu tarafından herhangi bir parametreden ayrı olarak gönderilen ve ayrıştırılan SQL ifadeleridir. Bu şekilde bir saldırganın zararlı SQL enjekte etmesi imkansızdır.

Bunu başarmak için temel olarak iki seçeneğiniz var:

  1. PDO 'yu kullanma (desteklenen herhangi bir veritabanı sürücüsü için):

     
    $stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
    
    $stmt->execute(array('name' => $name));
    
    foreach ($stmt as $row) {
        // do something with $row
    }
    
  2. MySQLi 'yi kullanma (MySQL için):

     
    $stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');
    $stmt->bind_param('s', $name); // 's' specifies the variable type => 'string'
    
    $stmt->execute();
    
    $result = $stmt->get_result();
    while ($row = $result->fetch_assoc()) {
        // do something with $row
    }
    

MySQL dışındaki bir veritabanına bağlanıyorsanız, başvurabileceğiniz sürücüye özgü ikinci bir seçenek vardır (örneğin, PostgreSQL için pg_prepare() ve pg_execute()). PDO evrensel bir seçenektir.

Bağlantıyı doğru şekilde kurma

MySQL veritabanına erişmek için PDO kullanırken gerçek hazırlanan ifadelerin varsayılan olarak kullanılmadığını unutmayın. Bunu düzeltmek için hazırlanan ifadelerin emülasyonunu devre dışı bırakmak zorundasınız. PDO kullanarak bağlantı oluşturmanın bir örneği:

 
$dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'pass');

$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

Yukarıdaki örnekte, hata modu kesinlikle gerekli değildir, eklemeniz önerilir, . Bu şekilde, bir şeyler ters gittiğinde senaryo Fatal Error ile bitmeyecektir. Ayrıca, geliştiriciye catch, throw n olarak PDOException n olan herhangi bir hatayı bildirme şansı verir.

zorunlu olan ise, PDO'ya öykünülmüş hazır ifadeleri devre dışı bırakmasını ve gerçek hazır ifadeleri kullanmasını söyleyen ilk setAttribute() satırıdır. Bu, ifadenin ve değerlerin MySQL sunucusuna göndermeden önce PHP tarafından ayrıştırılmadığından emin olur (olası bir saldırgana kötü niyetli SQL enjekte etme şansı vermez).

charset'u yapıcı seçeneklerinde ayarlayabilseniz de, PHP'nin 'eski' sürümlerinin (< 5.3.6) DSN'deki karakter kümesi parametresini sessizce yoksaydı.

Açıklama

Olan, prepare'a ilettiğiniz SQL ifadesinin veritabanı sunucusu tarafından ayrıştırılması ve derlenmesidir. Parametreleri belirleyerek (yukarıdaki örnekte ? veya :name gibi adlandırılmış bir parametre), veritabanı motoruna filtrelemek istediğiniz yeri söylersiniz. Sonra execute'u aradığınızda, hazırlanan ifade belirttiğiniz parametre değerleriyle birleştirilir.

Buradaki önemli şey, parametre değerlerinin SQL dizesiyle değil, compiled deyimi ile birleştirilmesidir. SQL enjeksiyonu, veritabanını göndermek için SQL oluştururken kötü niyetli dizeleri dahil etmek için betiği kandırarak çalışır. Bu nedenle, gerçek SQL'i parametrelerden ayrı olarak göndererek, istemediğiniz bir şeyle bitme riskini sınırlarsınız. Hazırlanan bir ifadeyi kullanırken gönderdiğiniz tüm parametreler sadece karakter dizisi olarak kabul edilir (veritabanı motoru bazı optimizasyonlar yapabilmesine rağmen, parametreler elbette sayılarla sonuçlanabilir). Yukarıdaki örnekte, $name değişkeni 'Sarah'; DELETE FROM employees içeriyorsa, sonuç yalnızca "'Sarah'; DELETE FROM employees" dizesinin bir araması olur ve boş bir masa .

Hazırlanan ifadeleri kullanmanın bir başka yararı, aynı ifadeyi aynı oturumda birçok kez yürütürseniz, yalnızca bir kez ayrıştırılıp derlenerek size hız kazandırır.

Oh, ve bir ek için nasıl yapılacağını sorduğundan, işte bir örnek (PDO kullanarak):

 
$preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)');

$preparedStatement->execute(array('column' => $unsafeValue));

Hazırlanan ifadeler dinamik sorgular için kullanılabilir mi?

Sorgu parametreleri için hazırlanmış ifadeleri yine de kullanabilirsiniz, ancak dinamik sorgunun yapısı parametrelenemez ve belirli sorgu özellikleri değiştirilemez.

Bu özel senaryolarda yapılacak en iyi şey, olası değerleri kısıtlayan bir beyaz liste filtresi kullanmaktır.

 
// Value whitelist
// $dir can only be 'DESC' otherwise it will be 'ASC'
if (empty($dir) || $dir !== 'DESC') {
   $dir = 'ASC';
}
    
8529
2019-02-25 22: 52: 46Z
  1. Sadece eklemek için burada başka bir yerde görmedim, başka bir savunma hattı web uygulaması güvenlik duvarı (WAF), sql enjeksiyon saldırılarını arayacak kurallar belirleyebilecek:
    2015-12-29 20: 11: 18Z
  2. Ayrıca, mysql_query'nin resmi belgeleri yalnızca bir sorgu yürütmeye izin verir; göz ardı edilir. Bu zaten kullanımdan kaldırılmış olsa bile, PHP 5.5.0 altında birçok sistem vardır ve bu fonksiyonu kullanabilir. php.net/manual/en/function.mysql-query.php
    2016-01-19 17: 40: 02Z
  3. Bu kötü bir alışkanlıktır ancak sorun sonrası bir çözümdür: Yalnızca SQL enjeksiyonu için değil, herhangi bir enjeksiyon türü için (örneğin, F3 framework v2) Eski bir hazır web siteniz veya uygulamanızın enjeksiyon kusurlarından muzdarip olması durumunda, çözümlerden biri, önyükleme sırasında $_POST gibi önceden belirlenmiş değerlere sahip olan supperglobal değerlerinizi yeniden atamaktır. PDO ile hala kaçmak mümkündür (bugünkü çerçeveler için de): substr ($pdo- > quote ($str, \PDO :: PARAM_STR), 1, -1)
    2016-01-24 15: 08: 30Z
  4. Bu yanıt, hazır bir ifadenin ne olduğunu açıklamada eksik - bir şey - isteğiniz sırasında çok fazla hazır ifade kullanırsanız ve bazen de hesap verirse 10x performans isabet. Daha iyi bir durum, PDO'yu parametre bağlama kapalıyken kullanmak, ancak ifade hazırlama kapalı olmalıdır.
    2016-11-18 08: 54: 29Z
  5. PDO'yu kullanmak daha iyidir, doğrudan sorgu kullanıyorsanız, mysqli :: escape_string
    kullandığınızdan emin olun.
    2017-11-06 08: 17: 19Z
  

Onaylanmadı Uyarı:   Bu yanıtlayıcı örnek kodu (sorunun örnek kodu gibi) PHP 5.5.0'da kullanımdan kaldırılan ve tamamen PHP 7.0.0'da kaldırılan PHP'nin MySQL uzantısını kullanır.

     

Güvenlik Uyarısı : Bu cevap, en iyi güvenlik uygulamalarıyla uyumlu değildir. Escaping önlemek için yetersiz SQL enjeksiyonu yerine hazırlanmış ifadeleri kullanın. Aşağıda belirtilen stratejiyi kullanmak kendi sorumluluğunuzdadır. (Ayrıca, mysql_real_escape_string(), PHP 7'de kaldırılmıştır.)

PHP'nin yeni bir sürümünü kullanıyorsanız, aşağıda belirtilen mysql_real_escape_string seçeneği artık kullanılamayacak (mysqli::escape_string modern bir eşdeğer olsa da). Bu günlerde mysql_real_escape_string seçeneği yalnızca PHP'nin eski bir sürümündeki eski kod için bir anlam ifade eder.


İki seçeneğiniz vardır - unsafe_variable'unuzdaki özel karakterlerden kaçmak veya parametreli hale getirilmiş bir sorgu kullanarak. Her ikisi de sizi SQL enjeksiyonundan koruyacaktır. Parametreli sorgu daha iyi bir uygulama olarak kabul edilir, ancak kullanmadan önce PHP'de daha yeni bir MySQL uzantısına geçilmesi gerekir.

İlk önce kaçan alt vuruş dizesini ele alacağız.

 
//Connect

$unsafe_variable = $_POST["user-input"];
$safe_variable = mysql_real_escape_string($unsafe_variable);

mysql_query("INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

//Disconnect

Ayrıca, mysql_real_escape_string işlevinin ayrıntılarına bakın.

Parametreli sorguyu kullanmak için MySQLi kullanmanız gerekir. /php.net/mysql "rel =" noreferrer "> MySQL fonksiyonları. Örneğinizi yeniden yazmak için, aşağıdaki gibi bir şeye ihtiyacımız var.

 
<?php
    $mysqli = new mysqli("server", "username", "password", "database_name");

    // TODO - Check that connection was successful.

    $unsafe_variable = $_POST["user-input"];

    $stmt = $mysqli->prepare("INSERT INTO table (column) VALUES (?)");

    // TODO check that $stmt creation succeeded

    // "s" means the database expects a string
    $stmt->bind_param("s", $unsafe_variable);

    $stmt->execute();

    $stmt->close();

    $mysqli->close();
?>

Okumak istediğiniz anahtar işlev mysqli::prepare olacaktır.

Ayrıca, diğerlerinin önerdiği gibi, bir soyutlama katmanını PDO .

Lütfen sorduğunuz durumun oldukça basit olduğunu ve daha karmaşık vakaların daha karmaşık yaklaşımlar gerektirebileceğini unutmayın.Özellikle:

1598
2019-05-09 03: 02: 10Z
  1. , mysql_real_escape_string'u kullanmak yeterli mi, yoksa ben de parametreli mi kullanmalıyım?
    2018-03-08 21: 29: 20Z
  2. @ peimanF. Yerel bir projede bile parametreli hale getirilmiş sorguları kullanmakta pratik yapmak. Parametreli sorgularda, SQL enjeksiyonunun olmayacağına garanti veriyorsunuz . Ancak, yanlış erişimden kaçınmak için (örneğin, HTML kodunu bir metne koymak gibi XSS enjeksiyonu) htmlentities ile örneğin,
    almamaya dikkat edin.
    2018-03-09 08: 02: 53Z
  3. @ peimanF. Sorguları parametreleştirmek ve değerleri bağlamak için iyi bir uygulama, ancak gerçek kaçış dizesi şimdilik iyi.
    2018-04-04 18: 03: 04Z
  4. mysql_real_escape_string()'un eksiksiz olması için dahil edilmesini anlıyorum, ancak önce hataya en çok yaklaşan yaklaşımı listeleme hayranı değilim. Okuyucu hemen ilk örneği hızlıca alabilir. İyi ki şu an kullanımdan kaldırıldı :)
    2018-12-05 00: 13: 01Z

Buradaki her cevap, sorunun yalnızca bir kısmını kapsar. Aslında, dinamik olarak ekleyebileceğimiz dört farklı sorgu bölümü var: -

  • bir dize
  • bir numara
  • bir tanımlayıcı
  • bir sözdizimi anahtar sözcüğü.

Hazırlanan ifadeler yalnızca ikisini kapsar.

Ancak bazen sorguyu daha da dinamik hale getirip işleçler veya tanımlayıcılar ekleyebiliriz. Bu yüzden farklı koruma tekniklerine ihtiyacımız olacak.

Genel olarak, böyle bir koruma yaklaşımı beyaz listeye dayanır.

Bu durumda, her dinamik parametre kodunuzda kodlanmış ve bu kümeden seçilmiş olmalıdır. Örneğin, dinamik sipariş yapmak için:

 
$orders  = array("name", "price", "qty"); // Field names
$key     = array_search($_GET['sort'], $orders)); // See if we have such a name
$orderby = $orders[$key]; // If not, first one will be set automatically. smart enuf :)
$query   = "SELECT * FROM `table` ORDER BY $orderby"; // Value is safe

Ancak, tanımlayıcıları korumak için başka bir yol daha var - kaçış. Belirtilen bir tanımlayıcınız olduğu sürece, onları iki katına çıkararak geri tepmelerden kaçabilirsiniz.

Başka bir adım olarak, hazırlanan ifadelerden bazı yer tutucular (sorgudaki gerçek değeri temsil eden bir vekil) kullanma hakkında gerçekten parlak bir fikir alabilir ve başka türden bir yer tutucu - bir tanımlayıcı yer tutucu icat edebiliriz.

Öyleyse, uzun öyküyü kısaltmak için: bu bir yer tutucusudur , hazırlanmış açıklama değil, gümüş bir madde işareti olarak kabul edilebilir.

Dolayısıyla, genel bir öneri olarak Yer tutucuları (ve tabii ki doğru şekilde işlenen bu yer tutucuları) kullanarak sorguya dinamik parçalar eklediğiniz sürece, sorgunuzun güvenli olduğundan emin olabilirsiniz .

Yine de, SQL sözdizimi anahtar kelimelerinde (AND, DESC gibi) bir sorun var, ancak beyaz listeleme bu durumda tek yaklaşım gibi görünüyor.

Güncelleme

SQL enjeksiyon koruması ile ilgili en iyi uygulamalar konusunda genel bir anlaşma olmasına rağmen, hala birçok kötü uygulama vardır. Ve bazıları PHP kullanıcılarının kafasında çok fazla yer almaktadır. Örneğin, bu sayfada (çoğu ziyaretçi tarafından görünmez olmasına rağmen) 80'den fazla silinmiş yanıt var - hepsi kötü kalite nedeniyle veya kötü ve güncel olmayan uygulamaları teşvik ederek topluluk tarafından kaldırıldı. Daha da kötüsüt, kötü cevapların bazıları silinmedi, daha çok başarılı oldu.

Örneğin, orada (1) (2) hala (3) birçok (4) cevaplar (5) , İkinci en çok oy alan yanıtı size manuel dizi ipten kaçmanızı önerir - güvensiz olduğu kanıtlanmış eski bir yaklaşım.

Veya yalnızca başka bir dize biçimlendirme yöntemi öneren ve hatta en son derde deva olarak övünen biraz daha iyi bir cevap var. Tabii ki öyle değil. Bu yöntem normal dize biçimlendirmesinden daha iyidir, ancak tüm dezavantajlarını korur: yalnızca dizelere uygulanabilir ve diğer el ile biçimlendirmelerde olduğu gibi, temelde isteğe bağlı, zorunlu olmayan bir önlemdir, herhangi bir türde insan hatasına eğilimlidir. p>

Bunların hepsinin OWASP veya "kaçan" ve SQL enjeksiyonlarından korunma arasında eşitlik olduğunu bildiren PHP kılavuzu .

PHP el kitabının uzun yıllar boyunca söylediklerinden bağımsız olarak, *_escape_string hiçbir şekilde verileri güvenli kılmaz ve asla amaçlanmamıştır. Dize dışındaki herhangi bir SQL parçası için işe yaramaz olmasının yanı sıra, manuel çıkış özelliği de yanlıştır, çünkü otomatikleştirmenin tersine manueldir.

Ve OWASP daha da kötüleştiriyor, daha da saçma bir şey olan kullanıcı girdisi 'den kaçmaya vurgu yapıyor: Enjeksiyon koruması bağlamında böyle bir kelime olmamalıdır. Her değişken potansiyel olarak tehlikelidir - kaynak ne olursa olsun! Veya başka bir deyişle - her değişkenin sorguya konması için uygun şekilde biçimlendirilmesi gerekir - kaynak ne olursa olsun. Önemli olan varış noktası. Bir geliştirici koyunları keçilerden ayırmaya başladığı an (bazı değişkenlerin "güvenli" olup olmadığını düşünerek) felakete ilk adımını attı. İfadelerin bile giriş noktasında toplu kaçarak, çok sihirli tırnaklar özelliğine benzeyen - zaten aşağılanmış, kullanımdan kaldırılmış ve kaldırılmış olduğunu söylemeye gerek yok.

Öyleyse, "kaçan" ların aksine, hazırlanan ifadeler gerçekten SQL enjeksiyonundan (geçerli olduğunda) koruyan önlemdir.

Hala ikna olmadıysanız, işte yazdığım adım adım bir açıklama. Hitchhiker's Guide SQL Injection önlemesine , burada tüm bu konuları detaylı bir şekilde açıkladım ve hatta tamamen kötü uygulamalar ve bunların ifşa edilmesine adanmış bir bölüm oluşturdum.

    
1028
2018-11-19 13: 30: 43Z
  1. Harika, iyi düşünülmüş bir makale. PHP’nin Sanitize filtrelerini kullanmanın bir tür (ama tam olarak değil) türden bir beyaz liste olduğunu ekleyebilirim. Örneğin, FILTER_SANITIZE_NUMBER_INT, yalnızca sayı karakterlerine izin verir; bu nedenle, tüm dizelerin değil, beyaz listeleme karakterlerinin listesidir. Hazırlanan ifadelerle birlikte, iyi bir "kemer ve askısı" yaklaşımı yapar.
    2016-01-20 17: 12: 39Z
  2. @ Sablefoste burada beyaz listeye ihtiyacınız yok. Herhangi bir temizleme işlemi gereksiz olacaktır. Uyulması gereken daha az kural var, daha az hata yapacaksınız. Herhangi bir doğrulama yapmanıza rağmen, uygulama mantığınız uğruna, ancak veritabanı için yapmayın.
    2016-01-20 17: 54: 41Z

için, PDO 'yu (PHP Veri Nesneleri) kullanmanızı öneririm parametreli hale getirilmiş SQL sorgularını çalıştırın.

Bu yalnızca SQL enjeksiyonuna karşı koruma sağlamakla kalmaz, sorguları da hızlandırır.

Ayrıca mysql_, mysqli_ ve pgsql_ yerine PDO kullanarak, uygulamanızı veri tabanı sağlayıcılarını değiştirmek zorunda kaldığınız nadir durumlarda, veritabanından biraz daha soyutlarsınız.

    
815
2017-12-25 14: 38: 32Z
  1. PDO MySQL DB'leri için PDO mysqli'yi sarmaz mı? Bu durumda kesinlikle mysqli'den daha hızlı olamaz. Yine de olsa tavsiye ederim. Mysqli API'sinin çok daha iyi bir arayüz.
    2016-05-26 13: 32: 10Z
  2. Parametreli sorguları kullanmak, sorguları hızlandıran şeydir. Teknik olarak mysqli çok küçük bir farkla daha da hızlı olabilir. Sunucunun sorguyu yanıtlamak için harcadığı gerçek süre, sarıcı kullandığınız için oluşabilecek zamanlamada herhangi bir fark yaratır. Ancak, mysqli veritabanına bağlanır. Farklı bir veritabanı motoru kullanmak istiyorsanız, mysqli kullanan tüm çağrıları değiştirmeniz gerekir. PDO için öyle değil.
    2016-05-26 13: 35: 47Z
  3. PDO maalesef dinamik order by'u desteklemiyor :(
    2018-01-18 16: 35: 26Z

PDO kullanın ve hazırlanmış sorgular.

($conn, PDO nesnesidir)

 
$stmt = $conn->prepare("INSERT INTO tbl VALUES(:id, :name)");
$stmt->bindValue(':id', $id);
$stmt->bindValue(':name', $name);
$stmt->execute();
    
604
2015-09-11 17: 02: 40Z
  1. wikipedia adresinden : Hazırlanan ifadeler SQL enjeksiyonuna karşı dirençlidir, çünkü daha sonra farklı bir protokol kullanılarak iletilen parametre değerlerinin doğru şekilde kaçması gerekmez. Orijinal ifade şablonu dış girdiden türetilmezse, SQL enjeksiyonu yapılamaz.
    2016-07-09 20: 08: 18Z

Gördüğünüz gibi, insanlar en çok hazırlıklı ifadeleri kullanmanızı önerir. Yanlış değil, ancak sorgunuz işlem başına yalnızca bir kez yürütüldüğünde, hafif bir performans cezası verilebilir.

Bu sorunla karşı karşıyaydım, ancak sanırım bu sorunu çok karmaşık bir şekilde çözdüm - bilgisayar korsanlarının tırnak kullanmaktan kaçınmaları için kullandıkları yöntem. Bunu öykünmüş hazır ifadelerle birlikte kullandım. Bunu tüm olası SQL enjeksiyon saldırılarını engellemek için kullanıyorum.

Benim yaklaşımım:

  • Girişin tam sayı olmasını beklerseniz, bunun really tamsayı olduğundan emin olun. PHP gibi değişken bir dilde bu çok önemlidir. Örneğin bu çok basit ama güçlü çözümü kullanabilirsiniz: sprintf("SELECT 1,2,3 FROM table WHERE 4 = %u", $input);

  • Tamsayıdan {hex hex} başka bir şey beklerseniz. Altıgen yaparsanız, tüm girdilerden kusursuzca kaçarsınız. C /C ++ 'da mysql_hex_string() adlı bir işlev vardır. , PHP’de bin2hex() adresini kullanabilirsiniz.

    Kaçan dizenin orijinal uzunluğunun 2x boyutunda olacağından endişe etmeyin, çünkü mysql_real_escape_string kullanıyor olsanız bile, PHP aynı olan ((2*input_length)+1) aynı kapasiteyi tahsis etmek zorundadır.

  • İkili verileri aktarırken bu hex yöntemi genellikle kullanılır, ancak SQL enjeksiyon saldırılarını önlemek için tüm verilerde kullanılmaması için hiçbir neden göremiyorum. Verileri 0x ile hazırlamanız veya bunun yerine UNHEX MySQL işlevini kullanmanız gerektiğini unutmayın.

Yani, örneğin, sorgu:

 
SELECT password FROM users WHERE name = 'root'

Olacak:

 
SELECT password FROM users WHERE name = 0x726f6f74

veya

 
SELECT password FROM users WHERE name = UNHEX('726f6f74')

Hex, mükemmel bir kaçış. Enjekte etmenin yolu yok.

UNHEX işlevi ile 0x öneki arasındaki fark

Yorumlarda bazı tartışmalar oldu, bu yüzden sonunda açıklığa kavuşturmak istiyorum. Bu iki yaklaşım çok benzerdir, ancak bazı yönlerden biraz farklıdır:

** 0x ** öneki yalnızca char, varchar, metin, blok, ikili vb. gibi sütunlar için kullanılabilir.
Ayrıca, boş bir dize eklemek üzereseniz, kullanımı biraz karmaşıktır. Tamamen '' ile değiştirmeniz gerekecek veya bir hata ile karşılaşacaksınız.

UNHEX () , herhangi bir sütun; boş dize için endişelenmenize gerek yok.


Hex yöntemleri genellikle saldırı olarak kullanılır

Bu hex yönteminin, tam sayıların karakter dizileri gibi olduğu ve sadece mysql_real_escape_string ile kaçtığı bir SQL enjeksiyon saldırısı olarak kullanıldığını unutmayın. Sonra tırnak kullanmaktan kaçınabilirsiniz.

Örneğin, böyle bir şey yaparsanız:

 
"SELECT title FROM article WHERE id = " . mysql_real_escape_string($_GET["id"])

bir saldırı size çok kolayca enjekte edebilir . Komut dosyanızdan dönen aşağıdaki enjekte edilmiş kodu düşünün:

  

SELECT ... NEREDE id = -1 sendikanın tümü information_schema.tables adresinden tablo_adı seç

ve şimdi yalnızca tablo yapısını açın:

  

SELECT ... WHERE id = -1 birliği, information_schema.column adresinden column_name öğesini seçer; burada table_name = 0x61727469636c65

Ve sonra sadece ne istediklerini veriyi seçin. Güzel değil mi?

Ancak, enjekte edilebilir bir sitenin kodlayıcısı onu altıgen yaparsa, sorgu böyle görüneceğinden enjeksiyon mümkün olmaz: SELECT ... WHERE id = UNHEX('2d312075...3635')

    
535
2017-12-25 14: 40: 04Z
  1. @ SumitGupta Evet, yaptınız. MySQL, + ile değil, CONCAT ile birleştirilir. Ve performansa göre: Performansı etkilediğini sanmıyorum çünkü mysql'nin verileri ayrıştırması gerekiyor ve kaynak dizge veya onaltılık olması önemli değil.
    2013-06-01 23: 49: 49Z
  2. @ YourCommonSense Hangi hatalarla karşılaşıyorsunuz? Belirli olun.
    2013-07-01 13: 52: 16Z
  3. @ YourCommonSense Konsepti anlayamıyorsunuz ... Eğer mysql'de dize olmak istiyorsanız, bu 'root' gibi bir alıntı yaparsınız ya da ondan altıgen yapabilirsiniz. numarayı girin ve string olarak gönderin, büyük olasılıkla '42' CHAR değil (42) ... '42' yazınız. hex 'de 0x726f6f74 değil 0x3432 olur
    2013-07-01 14: 07: 02Z
  4. @ YourCommonSense Söyleyecek hiçbir şeyim yok ... sadece lol ... hala sayısal alanlarda onaltılık denemek istiyorsanız, ikinci yoruma bakın. İşe yarayacağına bahse girerim.
    2013-07-01 14: 24: 52Z
  5. @ YourCommonSense, hala anlamadınız mı? 0x ve concat kullanamazsınız çünkü string boşsa bir hatayla biteceksiniz. Sorgunuza basit bir alternatif istiyorsanız, bunu 0x42 deneyin
    2013-08-01 12: 33: 59Z
  

Onaylanmadı Uyarı:   Bu yanıtlayıcı örnek kodu (sorunun örnek kodu gibi) PHP 5.5.0'da kullanımdan kaldırılan ve tamamen PHP 7.0.0'da kaldırılan PHP'nin SELECT title FROM article WHERE id = UNHEX(' . bin2hex($_GET["id"]) . ') uzantısını kullanır.

     

Güvenlik Uyarısı : Bu cevap, en iyi güvenlik uygulamalarıyla uyumlu değildir. Escaping önlemek için yetersiz SQL enjeksiyonu yerine hazırlanmış ifadeleri kullanın. Aşağıda belirtilen stratejiyi kullanmak kendi sorumluluğunuzdadır. (Ayrıca, MySQL, PHP 7'de kaldırılmıştır.)

     

ÖNEMLİ

     

SQL Injection'ı önlemenin en iyi yolu, kabul edilen cevabı gösterir.

     

Aura.Sql ve EasyDB , geliştiricilerin hazırlanan ifadeleri daha kolay kullanmalarını sağlar. Hazırlanan ifadelerin neden SQL enjeksiyonunu durdurma , bu mysql_real_escape_string() baypas ve yakın zamanda WordPress'teki Unicode SQL Injection güvenlik açığı düzeltildi .

Enjeksiyonönleme - mysql_real_escape_string ()

PHP bu saldırıları önlemek için özel olarak yapılmış bir işleve sahiptir. Yapmanız gereken tek şey, mysql_real_escape_string() bir fonksiyonun ağzını kullanmak.

mysql_real_escape_string, bir MySQL sorgusunda kullanılacak olan bir dizeyi alır ve güvenli bir şekilde kaçan tüm SQL enjeksiyon girişimleriyle aynı dizeyi döndürür. Temel olarak, bir kullanıcının MySQL güvenlikli bir alternatif, kaçan bir teklifle \'girebileceği sorunlu teklifleri (') değiştirir.

NOT: bu işlevi kullanmak için veritabanına bağlı olmanız gerekir!

//MySQL'e Bağlan

 mysql_real_escape_string

Daha fazla ayrıntıyı MySQL - SQL Enjeksiyon Önleme bölümünde bulabilirsiniz. .

    
484
2019-05-09 03: 05: 39Z
  1. Bu, eski mysql uzantısıyla yapabileceğiniz en iyisidir. Yeni kod için, mysqli veya PDO'ya geçmeniz önerilir.
    2013-02-26 12: 42: 46Z
  2. Bu 'bu saldırıları önlemek için özel olarak yapılmış bir işlev' ile aynı fikirde değilim. Ben
    $name_bad = "' OR 1'"; 
    
    $name_bad = mysql_real_escape_string($name_bad);
    
    $query_bad = "SELECT * FROM customers WHERE username = '$name_bad'";
    echo "Escaped Bad Injection: <br />" . $query_bad . "<br />";
    
    
    $name_evil = "'; DELETE FROM customers WHERE 1 or username = '"; 
    
    $name_evil = mysql_real_escape_string($name_evil);
    
    $query_evil = "SELECT * FROM customers WHERE username = '$name_evil'";
    echo "Escaped Evil Injection: <br />" . $query_evil;
    
    amacı her giriş veri dizesi için doğru SQL sorgusu oluşturmak için izin olduğunu düşünüyorum. Önleme sql-enjeksiyon, bu fonksiyonun yan etkisidir.
    2013-07-09 05: 01: 27Z
  3. doğru girdi veri dizelerini yazmak için işlevleri kullanmazsınız. Sadece kaçmaya ihtiyaç duymayan veya çoktan kaçmış olanları doğru yazıyorsunuz. mysql_real_escape_string () düşündüğünüz amaç doğrultusunda tasarlanmış olabilir, ancak tek değeri enjeksiyonu engellemektir.
    2014-03-12 22: 38: 22Z
  4. UYARI! mysql_real_escape_string yanılmaz değil .
    2014-04-25 14: 50: 11Z
  5. mysql_real_escape_string() şimdi kullanımdan kaldırıldı, bu nedenle artık uygun bir seçenek değil. Gelecekte PHP'den kaldırılacaktır. PHP veya MySQL milletinin tavsiye ettiği şeye devam etmek en iyisidir.
    2015-04-08 06: 41: 25Z

Bunun gibi temel bir şey yapabilirsiniz:

 mysql_real_escape_string

Bu her sorunu çözmeyecek, ama çok iyi bir basamak taşı. Değişkenin varlığını, biçimini (sayıları, harfleri vb.) Denetleme gibi belirgin öğeleri dışarıda bıraktım.

    
446
2019-06-18 10: 23: 01Z
  1. Örneğinizi denedim ve benim için iyi sonuç verdi. "Bu her sorunu çözmeyecek" ifadesini temizler misiniz?
    2012-04-22 20: 31: 05Z
  2. Dizeyi alıntı yapmazsanız, hala enjekte edilebilir. Örneğin,
    $safe_variable = mysqli_real_escape_string($_POST["user-input"], $dbConnection);
    mysqli_query($dbConnection, "INSERT INTO table (column) VALUES ('" . $safe_variable . "')");
    
    atın. Bu durumda, $q = "SELECT col FROM tbl WHERE x = $safe_var"; - $safe_var arasında ayar yapılması, fiyat teklifi olmaması nedeniyle işe yarar. Ayrıca 1 UNION SELECT password FROM users ve CONCAT kullanarak sorguya dizeler eklemek de mümkündür.
    2013-04-16 18: 06: 15Z
  3. @ Polinom Tamamen doğru, ancak bunu yalnızca yanlış kullanım olarak görüyorum. Doğru kullandığınız sürece kesinlikle çalışacaktır.
    2013-07-10 07: 30: 38Z
  4. UYARI! CHR yanılmaz değil .
    2014-04-25 14: 46: 52Z
  5. mysql_real_escape_string() şimdi kullanımdan kaldırıldı, bu nedenle artık uygun bir seçenek değil. Gelecekte PHP'den kaldırılacaktır. PHP veya MySQL milletinin tavsiye ettiği şeye devam etmek en iyisidir.
    2015-04-08 06: 37: 06Z

Ne yaparsanız kullanın, girdilerinizin mysql_real_escape_string veya başka bir iyi niyetli çöp tarafından yönetilmediğini kontrol ettiğinizden emin olun ve gerekirse magic_quotes veya sterilize etmek için neyse onu çalıştırın. >     

372
2017-12-25 14: 40: 48Z
  1. Gerçekten de; açık olan magic_quotes ile çalışan sadece kötü uygulamayı teşvik eder. Ancak, bazen ortamı her zaman bu seviyeye göre kontrol edemezsiniz - ya sunucuyu yönetme erişiminiz yoktur ya da uygulamanız bu tür bir yapılandırmaya bağlı (titreme) uygulamalarla bir arada bulunmalıdır. Bu nedenlerden dolayı, taşınabilir uygulamalar yazmak iyidir - bununla birlikte, dağıtım ortamını kontrol ediyorsanız, örneğin çünkü bu bir şirket içi uygulama veya yalnızca kendi ortamınızda kullanılacak.
    2011-04-24 17: 04: 56Z
  2. PHP 5.4'ten itibaren, 'sihirli tırnak' olarak bilinen kötülük öldü . Ve kötü çöplere iyi gelir.
    2013-01-16 22: 45: 58Z
  

Onaylanmadı Uyarı:   Bu yanıtlayıcı örnek kodu (sorunun örnek kodu gibi) PHP 5.5.0'da kullanımdan kaldırılan ve tamamen PHP 7.0.0'da kaldırılan PHP'nin stripslashes uzantısını kullanır.

     

Güvenlik Uyarısı : Bu cevap, en iyi güvenlik uygulamalarıyla uyumlu değildir. Kaçış için yetersiz SQL enjeksiyonunu engelle , bunun yerine hazırlanmış ifadeleri kullanın. Aşağıda belirtilen stratejiyi kullanmak kendi sorumluluğunuzdadır. (Ayrıca, MySQL, PHP 7'de kaldırılmıştır.)

Parametreli sorgu VE giriş doğrulama, gitme yoludur. mysql_real_escape_string() kullanılmasına rağmen, SQL enjeksiyonunun gerçekleşebileceği pek çok senaryo vardır.

Bu örnekler SQL enjeksiyonuna açık:

 mysql_real_escape_string()

veya

 
$offset = isset($_GET['o']) ? $_GET['o'] : 0;
$offset = mysql_real_escape_string($offset);
RunQuery("SELECT userid, username FROM sql_injection_test LIMIT $offset, 10");

Her iki durumda da, kapsüllemeyi korumak için

$order = isset($_GET['o']) ? $_GET['o'] : 'userid';
$order = mysql_real_escape_string($order);
RunQuery("SELECT userid, username FROM sql_injection_test ORDER BY `$order`");
'u kullanamazsınız.

Kaynak : Beklenmeyen SQL Enjeksiyonu (Kaçış Olduğunda) Yeterli Değil)

    
353
2019-05-09 02: 59: 38Z
  1. Kullanıcı girişinin uzunluk, tür ve sözdizimi için bir dizi tanımlanmış kurallar ve ayrıca iş kurallarına karşı kimliğini doğrulayan bir giriş doğrulama tekniği kullanırsanız, SQL enjeksiyonunu engelleyebilirsiniz .
    2015-09-15 08: 07: 17Z

Benim düşünceme göre, PHP uygulamanızda (veya herhangi bir web uygulamasında) SQL enjeksiyonunu genel olarak önlemenin en iyi yolu, uygulamanızın mimarisini düşünmektir. SQL enjeksiyonuna karşı korumanın tek yolu, veritabanına her konuştuğunuzda Doğru Olanı yapan özel bir yöntem veya işlev kullanmayı hatırlamaksa, yanlış yaparsınız. Bu şekilde, sorgunuzu kodunuzun bir noktasında doğru şekilde biçimlendirmeyi unutmayı unutmayın.

MVC modelini ve CakePHP veya CodeIgniter , muhtemelen doğru yoldur: Güvenli veritabanı sorguları oluşturma gibi genel görevler, bu tür çerçevelerde merkezi olarak uygulandı ve uygulandı. Web uygulamanızı mantıklı bir şekilde düzenlemenize yardımcı olur ve nesneleri tek tek SQL sorguları oluşturmaktan çok yükleme ve kaydetme hakkında daha fazla düşünmenizi sağlar.

    
304
2014-07-16 02: 05: 53Z
  1. İlk paragrafınızın önemli olduğunu düşünüyorum. Anlamak anahtardır. Ayrıca, herkes bir şirket için çalışmıyor. İnsanların büyük bir bölümü için, çerçeveler aslında anlama fikrine karşı çıkıyor. Temel bilgilerle yakınlaşmak, bir son tarihin altında çalışırken değerli olmayabilir, fakat dışarıdakiler kendi elleriyle kirlenmekten hoşlanırlar. Çerçeve geliştiricileri o kadar ayrıcalıklı değildir ki, başkaları asla hata yapmadıklarını öne sürmeli ve varsaymalıdır. Karar verme gücü hala önemlidir. Benim çerçevemin gelecekte başka bir düzeni değiştirmeyeceğini kim söyleyebilir?
    2017-01-04 16: 32: 46Z
  2. @ AnthonyRutledge Kesinlikle haklısın. Neler olup bittiğini anlamak çok önemlidir. Bununla birlikte, gerçek-denenmiş ve aktif bir şekilde kullanılan ve geliştirilen bir çerçevenin birçok sorunu çözüp çözme ve çok sayıda güvenlik açığı düzeltme olasılığı oldukça yüksektir. Kod kalitesi hakkında bir fikir edinmek için kaynağa bakmak iyi bir fikirdir. Test edilmemiş bir karışıklık ise, muhtemelen güvenli değildir.
    2017-01-04 18: 38: 17Z
  3. İşte. İşte. Güzel nokta. Bununla birlikte, birçok insanın bir MVC sistemini benimsemek için ders çalışabileceğini ve öğrenebileceğini kabul edersiniz, ancak herkes el ile çoğaltamaz (kontrolörler ve sunucu). Bir kişi bu noktada çok ileri gidebilir. Fıstık ezmeli pekan kurabiyemizi ısıtmadan önce kız arkadaşımın bana getirdiği mikrodalgamı anlamam gerekir mi? ; -)
    2017-01-04 19: 30: 19Z
  4. @ AnthonyRutledge Katılıyorum! Kullanım senaryosunun da bir fark yarattığını düşünüyorum: Kişisel ana sayfam için bir fotoğraf galerisi oluşturuyor muyum veya çevrimiçi bir bankacılık web uygulaması oluşturuyor muyum? İkinci durumda, güvenlik ayrıntılarını ve kullandığım çerçevenin bunları nasıl ele aldığını anlamak çok önemlidir.
    2017-01-04 20: 35: 02Z
  5. Ah, kendin yapması gereken güvenlik istisnası. Bak, hepsini riske atmaya ve kırılmaya istekli olmaya meyilliyim. :-) Dalga geçmek. Yeterince zaman, insanlar oldukça güvenli bir uygulama yapmayı öğrenebilirler. Çok fazla insan acele ediyor. Ellerini kaldırdılar ve çerçevelerin daha güvenli olduğunu varsaydılar. Ne de olsa, olayları test etmek ve çözmek için yeterli zamanları yok. Ayrıca, güvenlik özel çalışma gerektiren bir alandır. Programcıların algoritmaları ve tasarım modellerini anlamak sayesinde derinlemesine bildikleri bir şey değil.
    2017-01-04 20: 48: 10Z

saklı yordamları tercih ediyorum ( MySQL, güvenlik açısından 5.5'ten beri saklı yordamları desteklemektedir - avantajları -

  1. Çoğu veritabanı ( MySQL dahil) kullanıcı erişiminin saklı yordamları yürütmeyle sınırlandırılmasını sağlamak. İnce taneli güvenlik erişim kontrolü, ayrıcalık saldırılarının artmasını önlemek için kullanışlıdır. Bu, tehlike altındaki uygulamaların SQL'i doğrudan veritabanına karşı çalıştırmasını önler.
  2. Uygulamadan ham SQL sorgusunu soyutlarlar, böylece uygulama için veritabanı yapısının daha az bilgisi vardır. Bu, insanların veritabanının temel yapısını anlamalarını ve uygun saldırılar tasarlamalarını zorlaştırır.
  3. Yalnızca parametreleri kabul ederler, so Parametreli sorguların avantajları var. Tabii ki - IMO hala girişinizi dezenfekte etmeniz gerekiyor - özellikle saklı yordamın içinde dinamik SQL kullanıyorsanız.

Dezavantajları: -

  1. Onlar (saklı yordamlar) bakımı zordur ve çok hızlı bir şekilde çarpma eğilimindedirler. Bu, onları yönetmeyi bir sorun haline getirir.
  2. Dinamik sorgular için çok uygun değillerdir - eğer dinamik kodu parametre olarak kabul etmek için oluşturulmuşlarsa, birçok avantaj ihmal edilir.
290
2017-12-25 14: 43: 05Z

SQL enjeksiyonlarını ve diğer SQL kesmelerini önlemenin birçok yolu vardır. İnternette kolayca bulabilirsiniz (Google Arama). Elbette PDO en iyi çözümlerden biridir. Ama size SQL Injection’ın önlenmesi için bazı iyi bağlantılar önermek istiyorum.

SQL enjeksiyonu nedir ve nasıl önlenmeli?

SQL enjeksiyonu için PHP kılavuzu

PHP'de SQL enjeksiyonu ve önlenmesinin Microsoft açıklaması

ve diğerleri gibi MySQL ile SQL enjeksiyonunu önleme ve PHP

Şimdi, sorgunuzu SQL enjeksiyonundan neden engellemeniz gerekiyor?

Size bildirmek istiyorum: Neden kısa bir örnekle SQL enjeksiyonunu engellemeye çalışıyoruz:

Giriş doğrulama eşleşmesi için sorgu:

 '

Şimdi, eğer birisi (bilgisayar korsanı) koyarsa

 
$query="select * from users where email='".$_POST['email']."' and password='".$_POST['password']."' ";

ve bir şey parolası ....

Sorgu, yalnızca şu ana kadar sisteme ayrıştırılır:

 
$_POST['email']= admin@emali.com' OR '1=1

Diğer kısım atılacak. Peki ne olacak? Yetkili olmayan bir kullanıcı (hacker) şifresi olmadan yönetici olarak giriş yapabilir. Şimdi, yönetici /e-posta kullanıcısının yapabileceği her şeyi yapabilir. Bakın, SQL enjeksiyonunun engellenmemesi çok tehlikeli.

    
284
2017-12-25 14: 42: 16Z

Birinin PHP ve MySQL veya başka bir dataBase sunucusunu kullanmak isteyip istemediğini düşünüyorum:

  1. PDO (PHP Veri Nesneleri) öğrenmeyi düşünün - tek tip bir veri tabanı erişim katmanıdır Birden çok veritabanına erişim yöntemi.
  2. Öğrenmeyi düşünün MySQLi
  3. Şunun gibi yerel PHP işlevlerini kullanın: strip_tags , mysql_real_escape_string veya değişken sayısalsa, yalnızca
    $query="select * from users where email='admin@emali.com' OR '1=1';
    
    . PHP'de değişkenlerin türü hakkında daha fazla bilgi edinin burayı . PDO veya MySQLi gibi kütüphaneler kullanıyorsanız, her zaman PDO :: quote () ve mysqli_real_escape_string () .

Kütüphaneler örnekleri:

---- PDO

  

----- Yer tutucu yok - SQL enjeksiyonu için uygun! Kötüdür

 (int)$foo
  

----- Adsız yer tutucular

 
$request = $pdoConnection->("INSERT INTO parents (name, addr, city) values ($name, $addr, $city)");
  

----- Adlandırılmış yer tutucular

 
$request = $pdoConnection->("INSERT INTO parents (name, addr, city) values (?, ?, ?);

--- MySQLi

 
$request = $pdoConnection->("INSERT INTO parents (name, addr, city) value (:name, :addr, :city)");

P.S. :

PDO bu savaşı kolayca kazandı. On iki için destek farklı veritabanı sürücüleri ve adlandırılmış parametreleri, görmezden gelebiliriz küçük performans kaybı ve API'sına alışmak. Bir güvenlikten bakış açısı, geliştiricinin kullandığı sürece ikisi de güvende onların kullanılması gerektiği gibi

Ancak hem PDO hem de MySQLi oldukça hızlı olsa da, MySQLi gerçekleştirir ölçütlerde çok daha hızlı - hazırlanmamışlar için ~% 2.5 ifadeler ve hazır olanlar için ~% 6,5.

Ve lütfen her sorguyu veritabanınıza test edin - enjeksiyonu önlemenin daha iyi bir yolu.

    
260
2014-07-16 02: 27: 44Z

Mümkünse, parametrelerinizi yazın. Ancak, yalnızca int, bool ve float gibi basit türlerde çalışıyor.

 
$request = $mysqliConnection->prepare('
       SELECT * FROM trainers
       WHERE name = ?
       AND email = ?
       AND last_login > ?');

    $query->bind_param('first_param', 'second_param', $mail, time() - 3600);
    $query->execute();
    
252
2018-05-01 16: 51: 42Z
  1. Bu, hazırlanmış bir ifade yerine "kaçan bir değer" kullanabileceğim birkaç durumdan biri. Ve tamsayı türü dönüştürme işlemi son derece etkilidir.
    2016-03-13 22: 29: 37Z

Önbellek motorlarından yararlanmak istiyorsanız, Redis veya Memcached , belki DALMP bir seçenek olabilir. Saf MySQLi kullanır. Şunu kontrol edin: PHP kullanarak MySQL için DALMP Veritabanı Soyutlama Katmanı.

Ayrıca, sorgunuzu hazırlamadan önce argümanlarınızı 'hazırlayabilirsiniz', böylece dinamik sorgular oluşturabilir ve sonunda tamamen hazırlanmış bir ifade sorgusu alabilirsiniz. PHP kullanarak MySQL için DALMP Veri Tabanı Soyutlama Katmanı

    
228
2017-12-25 14: 45: 42Z

PDO'nun nasıl kullanılacağından emin olmayanlar için (

$unsafe_variable = $_POST['user_id'];

$safe_variable = (int)$unsafe_variable ;

mysqli_query($conn, "INSERT INTO table (column) VALUES ('" . $safe_variable . "')");
işlevlerinden geliyor), bir tek bir dosya olan çok, çok basit bir PDO sarmalayıcısı . Uygulamaların yapılması gereken tüm genel işleri yapmanın ne kadar kolay olduğunu göstermek için var. PostgreSQL, MySQL ve SQLite ile çalışır.

Temel olarak, PDO işlevlerinin gerçek hayatta nasıl kullanılacağını görmek için bu kılavuzu okuyun, kılavuzu okuyunken Değerleri istediğiniz istediğiniz biçimde saklamayı ve almayı kolaylaştırmak için.

  

Tek bir sütun istiyorum

 mysql_      

Bir dizi (key = > değer) sonucu (seçim kutusu yapmak için) sonuçlar istiyorum

 
$count = DB::column('SELECT COUNT(*) FROM `user`);
     

Tek bir satır sonucu istiyorum

 
$pairs = DB::pairs('SELECT `id`, `username` FROM `user`);
     

Bir dizi sonuç istiyorum

 
$user = DB::row('SELECT * FROM `user` WHERE `id` = ?', array($user_id));
    
219
2017-12-25 14: 47: 00Z

Bu PHP işlevini

$banned_users = DB::fetch('SELECT * FROM `user` WHERE `banned` = ?', array(TRUE));
kullanarak hızlı bir şekilde iyi bir önleme alabilirsiniz.

Örneğin:

 mysql_escape_string()

SELECT * FROM users WHERE name = '".mysql_escape_string($name_from_html_form)."'
- Bir mysql_query içinde kullanmak için bir dize çıkarır

Daha fazla önleme için, sonuna ekleyebilirsiniz ...

 mysql_escape_string

Sonunda anladın:

 
wHERE 1=1   or  LIMIT 1
    
217
2017-12-25 14: 46: 07Z

SQL ifadelerinde özel karakterlerden kaçmak için birkaç kural.

MySQL kullanmayın, bu uzantı kullanımdan kaldırıldı, kullanın MySQLi veya PDO .

MySQLi

Bir dizedeki özel karakterlerden elle kaçmak için mysqli_real_escape_string işlevi. Doğru karakter kümesi mysqli_set_charset .

Örnek:

 
SELECT * FROM users WHERE name = '".mysql_escape_string($name_from_html_form)."' LIMIT 1

Hazırlanan ifadelerle değerlerin otomatik olarak kaçılması için mysqli_prepare , ve mysqli_stmt_bind_param ve ilgili bağlama değişkenlerinin türlerinin olması gerektiği yerlerde uygun bir dönüşüm için sağlandı:

Örnek:

 
$mysqli = new mysqli( 'host', 'user', 'password', 'database' );
$mysqli->set_charset( 'charset');

$string = $mysqli->real_escape_string( $string );
$mysqli->query( "INSERT INTO table (column) VALUES ('$string')" );

Hazırlanan ifadeler veya mysqli_real_escape_string kullanıyor olsanız da, çalıştığınız giriş verilerinin türünü her zaman bilmeniz gerekir.

Hazırlanan bir ifade kullanırsanız, mysqli_stmt_bind_param işlevi için değişken türlerini belirtmeniz gerekir.

Ve mysqli_real_escape_string kullanımı, adından da anlaşılacağı gibi, bir dizgede özel karakterlerden kaçmak için kullanılır, bu yüzden tamsayıları güvenli hale getirmez. Bu işlevin amacı, SQL deyimlerindeki dizelerin kırılmasını ve veritabanına sebep olabileceği zararı önlemektir. mysqli_real_escape_string, özellikle sprintf ile birleştirildiğinde düzgün kullanıldığında yararlı bir işlevdir.

Örnek:

 
$stmt = $mysqli->prepare( "INSERT INTO table ( column1, column2 ) VALUES (?,?)" );

$stmt->bind_param( "is", $integer, $string );

$stmt->execute();
    
199
2017-12-25 14: 48: 22Z
  1. Soru çok geneldir. Yukarıdaki bazı büyük cevaplar, ancak çoğu hazırlanan ifadeler öneriyor. MySQLi async hazırlanmış ifadeleri desteklemiyor, bu nedenle sprintf bu durum için harika bir seçenek gibi görünüyor.
    2016-04-23 22: 33: 00Z

Bu soruna basit bir alternatif, veritabanına kendisinde uygun izinler verilerek çözülebilir. Örneğin: eğer bir MySQL veritabanı kullanıyorsanız, veritabanına terminal veya UI üzerinden veri tabanına girin ve aşağıdaki komutu uygulayın:

 
$string = "x' OR name LIKE '%John%";
$integer = '5 OR id != 0';

$query = sprintf( "SELECT id, email, pass, name FROM members WHERE email ='%s' AND id = %d", $mysqli->real_escape_string( $string ), $integer );

echo $query;
// SELECT id, email, pass, name FROM members WHERE email ='x\' OR name LIKE \'%John%' AND id = 5

$integer = '99999999999999999999';
$query = sprintf( "SELECT id, email, pass, name FROM members WHERE email ='%s' AND id = %d", $mysqli->real_escape_string( $string ), $integer );

echo $query;
// SELECT id, email, pass, name FROM members WHERE email ='x\' OR name LIKE \'%John%' AND id = 2147483647

Bu, kullanıcıyı yalnızca belirtilen sorgunla sınırlandırılması için kısıtlar. Silme iznini kaldırın, böylece veriler PHP sayfasından gönderilen sorgudan asla silinmeyecek. Yapılması gereken ikinci şey, MySQL'in izinleri ve güncellemeleri yenilemesi için ayrıcalıkları temizlemektir.

 
 GRANT SELECT, INSERT, DELETE ON database TO username@'localhost' IDENTIFIED BY 'password';

floş hakkında daha fazla bilgi edinin.

Kullanıcının geçerli ayrıcalıklarını görmek için aşağıdaki sorguyu tetikleyin.

 
FLUSH PRIVILEGES; 

GRANT hakkında daha fazla bilgi edinin.

    
180
2018-11-19 13: 25: 25Z
  1. Bu cevap esasen yanlıştır çünkü enjeksiyonun önlenmesini önlemeye yardımcı olmaz, sadece sonuçları yumuşatmaya çalışır. Boşuna.
    2016-05-20 11: 00: 18Z
  2. Doğru, bir çözüm sağlamaz, ancak işleri engellemek için elden önce yapabileceğiniz şeydir.
    2016-05-25 18: 25: 35Z
  3. @ Apurv Amacım veritabanınızdaki özel bilgileri okumaksa, DELETE iznine sahip olmanın hiçbir anlamı yoktur.
    2016-10-05 14: 03: 41Z
  4. @ AlexHolsgrove: Sakin ol, sadece sonuçları yumuşatmak için iyi uygulamalar öneriyordum.
    2016-10-14 20: 59: 30Z
  5. @ Apurv "Sonuçları yumuşatmak" istemezsiniz, buna karşı korumak için mümkün olan her şeyi yapmak istersiniz. Yine de dürüst olmak gerekirse, doğru kullanıcı erişimini ayarlamak önemlidir, ancak OP'nin istediği şey değildir.
    2016-10-14 21: 08: 42Z
  

Güvenlik Uyarısı : Bu cevap aynı sırada değilgüvenlik ile en iyi yöntemler. Kaçış için yetersiz SQL enjeksiyonunu engelle , bunun yerine hazırlanmış ifadeleri kullanın. Aşağıda belirtilen stratejiyi kullanmak kendi sorumluluğunuzdadır. (Ayrıca,

select * from mysql.user where User='username';
, PHP 7'de kaldırılmıştır.)      

Kaldırıldı Uyarı : MySQL eklentisi şu anda kullanımdan kaldırılmıştır. PDO uzantısını

kullanmanızı öneririz

Web uygulamamın SQL enjeksiyonuna karşı savunmasız kalmasını önlemek için üç farklı yol kullanıyorum.

  1. PHP 'de önceden tanımlanmış bir işlev olan mysql_real_escape_string()’un kullanımı ve bu kod aşağıdaki karakterlere ters eğik çizgi ekleyin: mysql_real_escape_string(), \x00, \n, \r, \, ' ve ". SQL enjeksiyon olasılığını en aza indirmek için giriş değerlerini parametre olarak iletin.
  2. En gelişmiş yöntem PDO'ları kullanmaktır.

Umarım bu size yardımcı olur.

Aşağıdaki sorguyu düşünün:

\x1a

mysql_real_escape_string () burada korunmayacaktır. Değişkenlerinizin etrafında tek tırnak ('') kullanırsanız, sorgunuz içinde sizi koruyan şey budur. İşte bunun için aşağıda bir çözüm:

$iId = mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";

Bu sorusu hakkında bazı yanıtlar var Bu.

Bence PDO kullanmak en iyi seçenek.

Düzenle:

$iId = (int) mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";, PHP 5.5.0 itibariyle kullanımdan kaldırılmıştır. MySQL veya PDO kullanın.

mysql_real_escape_string () işlevine bir alternatif:

 mysql_real_escape_string()

Örnek:

 
string mysqli_real_escape_string ( mysqli $link , string $escapestr )
    
170
2019-05-09 03: 00: 18Z

Birçok faydalı cevapla ilgili olarak, bu konuya bazı değerler eklemeyi umuyorum. SQL enjeksiyon kullanıcı girişleri (kullanıcı tarafından doldurulan ve daha sonra sorguların içinde kullanılan girişler) aracılığıyla yapılabilecek bir saldırıdır, SQL enjeksiyon kalıpları doğru sorgu sözdizimidir, buna adlandırabiliriz: kötü nedenlerden dolayı kötü sorgular, varsayabiliriz üç güvenlik ilkesini (Gizlilik, Bütünlük, Kullanılabilirlik) etkileyen gizli bilgiler edinmeye (erişim kontrolünü atlayarak) kötü bir insan olun.

Şimdi noktamız, SQL enjeksiyon saldırıları, PHP kullanarak SQL enjeksiyon saldırısı nasıl önlenir), daha gerçekçi olmak, kullanıcı filtrelemesinde veri filtrelemek veya girdi verilerini silmek gibi güvenlik tehditlerini önlemek. PHP veya başka bir programlama dilini kullanan bu sorgu içindeki veriler durum değildir veya hazır ifade veya şu anda SQL enjeksiyon engellemesini destekleyen diğer araçlar gibi modern teknolojiyi kullanmak için daha fazla kişi tarafından önerildiği gibi, bu araçların artık mevcut olmadığını mı düşünüyorsunuz? Başvurunuzu nasıl güvence altına alıyorsunuz?

SQL enjeksiyonuna karşı yaklaşımım: veritabanına göndermeden önce kullanıcı girişi verilerinin temizlenmesi (herhangi bir sorguda kullanmadan önce).

Şunun için veri filtreleme (Güvenli olmayan verileri güvenli verilere dönüştürme) PDO ve MySQLi kullanılamaz, başvurunuzu nasıl güvence altına alabilirsiniz? Beni onları kullanmaya zorluyor musun? PHP dışındaki diğer dillerden ne haber? Sadece belirli diller için değil, daha geniş sınır için kullanılabildiği için genel fikirler sunmayı tercih ediyorum.

  1. SQL kullanıcısı (kullanıcı imtiyazını sınırlandırmak): en yaygın SQL işlemleri (SELECT, UPDATE, INSERT) 'dir, öyleyse neden gerekli olmayan bir kullanıcıya UPDATE ayrıcalığı veriyor? Örneğin, giriş yapın ve sayfa arayın , yalnızca SELECT kullanıyor, öyleyse neden bu kullanıcılarda yüksek ayrıcalıklı DB kullanıcıları kullanılıyor? KURAL: tüm ayrıcalıklar için bir veritabanı kullanıcısı oluşturmayın, tüm SQL işlemleri için, kolay kullanım için kullanıcı adı olarak şemanızı (kullanıcı, seçim kullanıcısı, güncelleme kullanıcı) oluşturabilirsiniz.

bkz. En az ayrıcalık prensibi

  1. Veri filtreleme: Herhangi bir sorgu oluşturmadan önce kullanıcı girişi doğrulanmalı ve filtrelenmelidir, programcılar için her kullanıcı girişi değişkeni için bazı özellikler tanımlamak önemlidir: veri türü, veri deseni ve veri uzunluğu . (x ve y) arasında bir sayı olan bir alan, kesin bir kural kullanılarak tam olarak doğrulanmalıdır, bir dize (metin) olan bir alan için: desen durum böyledir, örneğin, username consadece bazı karakterleri söylemek [a-zA-Z0-9_-.] diyelim ki uzunluğu x ve n (tamsayı, x Kural: kesin filtreler oluşturma ve doğrulama kuralları benim için en iyi yöntemdir.

  2. Diğer araçları kullanın: Burada, hazırlanan ifadeyi (parametreli sorgu) ve Saklı yordamları hazırladığınızı da kabul edeceğim, buradaki dezavantajlar, bu yolların çoğu kullanıcı için olmayan ileri beceriler gerektirdiği, buradaki temel fikir SQL sorgusu ile içinde kullanılan veriler arasında ayrım yapmak, her iki yaklaşım da güvenli olmayan verilerle bile kullanılabilir, çünkü kullanıcı-giriş verileri burada (herhangi bir veya x = x) gibi orijinal sorguya bir şey eklememektedir. Daha fazla bilgi için, lütfen OWASP SQL Enjeksiyonu Önleme Hile Sayfası 'nı okuyun. li>

Şimdi, ileri düzey bir kullanıcıysanız, bu savunmayı istediğiniz gibi kullanmaya başlayın, ancak yeni başlayanlar için saklı yordamı hızlı bir şekilde uygulayamıyorlarsa ve ifadeyi hazırlamazlarsa, girdi verilerini olabildiğince filtrelemek daha iyidir.

Son olarak, kullanıcının kullanıcı adını girmek yerine bu metni aşağıya gönderdiğini düşünelim:

 
$iId = $mysqli->real_escape_string("1 OR 1=1");
$mysqli->query("SELECT * FROM table WHERE id = $iId");

Bu girdi, önceden hazırlanmış herhangi bir açıklama ve saklı yordam olmadan erken kontrol edilebilir, ancak kullanıcı tarafında veri filtreleme ve doğrulama işleminden sonra başlatılanları kullanarak güvenli tarafta olmak.

Son nokta, daha fazla çaba ve karmaşıklık gerektiren beklenmeyen davranışları tespit etmektir; normal web uygulamaları için önerilmez. Yukarıdaki kullanıcı girişinde beklenmeyen davranışlar, SELECT, UNION, IF, SUBSTRING, BENCHMARK, SHA, root olup, bu kelimeler algılandığında, girişi önleyebilirsiniz.

UPDATE1:

Bir kullanıcı bu gönderinin yararsız olduğunu yorumladı, tamam! İşte OWASP.OR :

  

Birincil savunmalar:

      Seçenek # 1: Hazırlanan İfadelerin Kullanımı (Parametreli Sorgular)
      Seçenek # 2: Saklı Prosedürlerin Kullanımı
      Seçenek # 3: Tüm Kullanıcı Tarafından Girilen Girişten Kaçış

  Ek savunma:

      Ayrıca Uygula: En Az Ayrıcalık
      Ayrıca Gerçekleştir: Beyaz Liste Giriş Doğrulaması

Bildiğiniz gibi, bir makaleyi iddia etmek geçerli bir argümanla, en az bir referansla desteklenmelidir! Aksi takdirde, saldırı ve kötü iddia olarak kabul edilir!

Update2:

PHP kılavuzundan, PHP: Hazırlanan Tablolar - Kılavuz :

  

Kaçma ve SQL enjeksiyonu

     

Bağlı değişkenler, sunucu tarafından otomatik olarak çıkarılır.   sunucu, kaçan değerlerini uygun yerlere yerleştirir.   yürütmeden önce deyim şablonu. Bir ipucu sağlanmalıdır   uygun değişken oluşturmak için bağlı değişken türünün sunucusu   dönüştürme. Daha fazla bilgi için mysqli_stmt_bind_param () işlevine bakın.   bilgi.

     

Sunucudaki değerlerden otomatik olarak kaçma bazen   SQL enjeksiyonunu önlemek için bir güvenlik özelliği olarak kabul edilir. Aynısı   Hazırlanmamış ifadelerle güvenlik derecesi elde edilebilirse   giriş değerleri doğru şekilde kaçtı.

Update3:

Hazırlanan ifadeyi kullanırken PDO ve MySQLi'nin sorguyu MySQL sunucusuna nasıl gönderdiğini bilmek için test senaryoları oluşturdum:

PDO:

 
[1] UNION SELECT IF(SUBSTRING(Password,1,1)='2',BENCHMARK(100000,SHA1(1)),0) User,Password FROM mysql.user WHERE User = 'root'

Sorgu Günlüğü:

 
$user = "''1''"; //Malicious keyword
$sql = 'SELECT * FROM awa_user WHERE userame =:username';
$sth = $dbh->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$sth->execute(array(':username' => $user));

MySQLi:

 
    189 Query SELECT * FROM awa_user WHERE userame ='\'\'1\'\''
    189 Quit

Sorgu Günlüğü:

 
$stmt = $mysqli->prepare("SELECT * FROM awa_user WHERE username =?")) {
$stmt->bind_param("s", $user);
$user = "''1''";
$stmt->execute();

Hazırlanan bir ifadenin verilerden de kaçtığı, başka hiçbir şey olmadığı açıktır.

Yukarıdaki açıklama

    188 Prepare   SELECT * FROM awa_user WHERE username =?
    188 Execute   SELECT * FROM awa_user WHERE username ='\'\'1\'\''
    188 Quit
'da da belirtildiği gibi, bu, The automatic escaping of values within the server is sometimes considered a security feature to prevent SQL injection. The same degree of security can be achieved with non-prepared statements, if input values are escaped correctly gibi veri doğrulamasının herhangi bir sorgu göndermeden önce tamsayı değerleri için iyi bir fikir olduğunu kanıtlar; ayrıca, sorguyu göndermeden önce kötü amaçlı kullanıcı verilerinin engellenmesi doğru ve geçerli bir yaklaşım .

Lütfen daha fazla ayrıntı için bu soruya bakın:

Kaynaklar:

  1. SQL Enjeksiyon Hile Sayfası
  2. SQL Enjeksiyonu
  3. Bilgi güvenliği
  4. Güvenlik İlkeleri
  5. Veri doğrulaması
169
2018-11-19 13: 23: 53Z

Basit bir yol, CodeIgniter veya Laravel , filtreleme ve aktif kayıt gibi yerleşik özelliklere sahiptir, böylece bu nüanslar için endişelenmenize gerek kalmaz.

    
167
2017-12-25 14: 50: 57Z
  1. Bence sorunun asıl amacı, böyle bir çerçeve kullanmadan bunu yapmaktır.
    2018-01-02 16: 16: 23Z

** Uyarı: Bu cevapta açıklanan yaklaşım yalnızca çok özel senaryolar için geçerlidir ve SQL enjeksiyon saldırıları yalnızca intval()'u enjekte edememeye bağlı olmadığından güvenli değildir. **

Saldırganlar, PHP'nin X=Y değişkeni veya URL'nin sorgu dizesiyle forma girmeye çalışıyorsa, güvenli olmadıklarında bunları yakalayabilirsiniz.

 $_GET

Çünkü

RewriteCond %{QUERY_STRING} ([0-9]+)=([0-9]+)
RewriteRule ^(.*) ^/track.php
, 1=1, 2=2, 1=2, 2=1, vb ... saldırganın SQL veritabanına ortak sorulardır. Belki ayrıca birçok bilgisayar korsanlığı uygulaması tarafından da kullanılır.

Ancak, sitenizden güvenli bir sorguyu yeniden yazmamanız gerektiğine dikkat etmeniz gerekir. Yukarıdaki kod, (size bağlı) yeniden yazmak veya yönlendirmek için saldırganın IP adresi veya ÇEREZLERİNİ EVLE, tarihçe, tarayıcı veya diğer hassas bilgilerden arındırın; yetkililerle iletişim kurma.

    
140
2017-12-25 14: 52: 27Z

PHP ve MySQL için çok fazla cevap var, ancak ici8 sürücülerinin düzenli kullanımını yanı sıra SQL enjeksiyonunu önlemek için PHP ve Oracle kodu:

 1+1=2     
127
2016-05-25 13: 51: 10Z
  1. Lütfen oci_bind_by_name parametrelerini açıklayın.
    2019-01-01 04: 10: 16Z

İyi bir fikir, Idiorm 'nesne-ilişkisel eşleştiricisi' kullanmaktır. /a>:

 
$conn = oci_connect($username, $password, $connection_string);
$stmt = oci_parse($conn, 'UPDATE table SET field = :xx WHERE ID = 123');
oci_bind_by_name($stmt, ':xx', $fieldval);
oci_execute($stmt);

Yalnızca SQL enjeksiyonlarından değil, sözdizimi hatalarından da sizi kurtarır! Ayrıca, bir kerede ve birden fazla bağlantıda birden çok sonuca eylemleri filtrelemek veya uygulamak için yöntem zincirine sahip model koleksiyonlarını destekler.

    
123
2017-12-25 14: 54: 11Z
  

Onaylanmadı Uyarı:   Bu yanıtlayıcı örnek kodu (sorunun örnek kodu gibi) PHP 5.5.0'da kullanımdan kaldırılan ve tamamen PHP 7.0.0'da kaldırılan PHP'nin

$user = ORM::for_table('user')
->where_equal('username', 'j4mie')
->find_one();

$user->first_name = 'Jamie';
$user->save();

$tweets = ORM::for_table('tweet')
    ->select('tweet.*')
    ->join('user', array(
        'user.id', '=', 'tweet.user_id'
    ))
    ->where_equal('user.username', 'j4mie')
    ->find_many();

foreach ($tweets as $tweet) {
    echo $tweet->text;
}
uzantısını kullanır.      

Güvenlik Uyarısı : Bu cevap, en iyi güvenlik uygulamalarıyla uyumlu değildir. kaçmayı SQL enjeksiyonunu önlemek için yetersiz bırakmak , yerine hazırlanmış ifadeleri kullanmak yetersizdir. Aşağıda belirtilen stratejiyi kendi sorumluluğunuzdadır. (Ayrıca, MySQL PHP 7'de kaldırılmıştır.)

PDO ve MYSQLi , SQL enjeksiyonlarını önlemek için iyi bir uygulamadır, ancak gerçekten MySQL fonksiyonları ve sorguları ile çalışmak istiyorsanız, kullanmak daha iyi olur

mysql_real_escape_string

 mysql_real_escape_string()

Bunu engellemek için daha fazla yetenek var: tanımlanmış gibi - eğer girdi bir string, sayı, char veya array ise, bunu algılayacak çok fazla dahili fonksiyon vardır. Ayrıca, giriş verilerini kontrol etmek için bu işlevleri kullanmak daha iyi olacaktır.

is_string

 
$unsafe_variable = mysql_real_escape_string($_POST['user_input']);

is_numeric

 
$unsafe_variable = (is_string($_POST['user_input']) ? $_POST['user_input'] : '');

Girdi verilerini

$unsafe_variable = (is_numeric($_POST['user_input']) ? $_POST['user_input'] : '');
ile kontrol etmek için bu işlevleri kullanmak çok daha iyidir.     
120
2019-05-09 03: 00: 59Z
  1. Ayrıca, $_POST dizi üyelerini is_string ()
    ile kontrol etmenin hiçbir anlamı yoktur.
    2014-01-18 07: 06: 05Z
  2. UYARI! mysql_real_escape_string yanılmaz değil .
    2014-04-25 14: 54: 01Z
  3. mysql_real_escape_string() şimdi kullanımdan kaldırıldı, bu nedenle artık uygun bir seçenek değil. Gelecekte PHP'den kaldırılacaktır. PHP veya MySQL milletinin tavsiye ettiği şeye devam etmek en iyisidir.
    2015-04-08 06: 53: 46Z
  4. Tema: Kullanıcının gönderdiği verilere güvenmeyin. Beklediğiniz herhangi bir şey, çalıştırmakta olduğunuz SQL sorgusunun bir parçası olması gereken özel karakterli veya mantıksal mantık içeren bir çöp veridir. $_POST değerlerini SQL kısmı yerine yalnızca veri olarak saklayın.
    2017-12-02 07: 39: 58Z

Bu küçük işlevi birkaç yıl önce yazdım:

 mysql_real_escape_string

Bu, ifadelerin tek satırlık bir C # -ish String.Format gibi çalıştırılmasını sağlar:

 
function sqlvprintf($query, $args)
{
    global $DB_LINK;
    $ctr = 0;
    ensureConnection(); // Connect to database if not connected already.
    $values = array();
    foreach ($args as $value)
    {
        if (is_string($value))
        {
            $value = "'" . mysqli_real_escape_string($DB_LINK, $value) . "'";
        }
        else if (is_null($value))
        {
            $value = 'NULL';
        }
        else if (!is_int($value) && !is_float($value))
        {
            die('Only numeric, string, array and NULL arguments allowed in a query. Argument '.($ctr+1).' is not a basic type, it\'s type is '. gettype($value). '.');
        }
        $values[] = $value;
        $ctr++;
    }
    $query = preg_replace_callback(
        '/{(\\d+)}/', 
        function($match) use ($values)
        {
            if (isset($values[$match[1]]))
            {
                return $values[$match[1]];
            }
            else
            {
                return $match[0];
            }
        },
        $query
    );
    return $query;
}

function runEscapedQuery($preparedQuery /*, ...*/)
{
    $params = array_slice(func_get_args(), 1);
    $results = runQuery(sqlvprintf($preparedQuery, $params)); // Run query and fetch results.   
    return $results;
}

Değişken tipi dikkate alındığında kaçar. Tablo ve sütun adlarını parametreleştirmeye çalışırsanız, her dizgiyi geçersiz sözdizimi olan tırnaklara koyarken başarısız olur.

GÜVENLİK GÜNCELLEME: Önceki

runEscapedQuery("INSERT INTO Whatever (id, foo, bar) VALUES ({0}, {1}, {2})", $numericVar, $stringVar1, $stringVar2);
sürüm, kullanıcı verilerine {#} belirteçler ekleyerek enjeksiyonlara izin verdi. Bu str_replace sürümü, değiştirme işlemi bu belirteçleri içeriyorsa sorun yaratmaz.     
86
2017-12-25 14: 55: 27Z
preg_replace_callback
kaynak yerleştirildi İşte