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


Kullanıcı girdisi bir SQL sorgusuna değiştirilmeden eklenirse, uygulama savunmasız hale gelir. SQL enjeksiyonuAşağıdaki örnekte olduğu gibi:

$unsafe_variable = $_POST['user_input']; 

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

Çünkü kullanıcı böyle bir şey girebilir value'); DROP TABLE table;--ve sorgu şöyle olur:

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

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


2781


Menşei




Cevaplar:


Hazırlanmış ifadeleri ve parametreli 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'i enjekte etmesi imkansızdır.

Temel olarak bunu başarmak için iki seçeneğiniz var:

  1. kullanma PDO (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. kullanma MySQLi (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ışında bir veritabanına bağlanıyorsanız, başvurabileceğiniz sürücüye özgü ikinci bir seçenek vardır (ör. pg_prepare() ve pg_execute() PostgreSQL için). PDO evrensel bir seçenektir.

Bağlantının doğru ayarlanması

Kullanırken kullanırken unutmayın PDO MySQL veritabanına erişmek için gerçek hazırlanan ifadeler varsayılan olarak kullanılmaz. Bunu düzeltmek için hazırlanmış ifadelerin öykünmesini devre dışı bırakmanız gerekir. PDO kullanarak bir bağlantı oluşturma ö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. ama eklemesi tavsiye edilir. Bu şekilde komut dosyası bir Fatal Error bir şeyler ters gittiğinde Ve geliştiriciye şans veriyor. catch herhangi bir hata (lar) thrown gibi PDOExceptions.

Nedir zorunluAncak, ilk setAttribute() PDO'ya taklit edilen hazırlanmış deyimleri devre dışı bırakmasını ve kullanmasını söyleyen satır gerçek hazırlanan ifadeler. Bu, ifadenin ve değerlerin PHP tarafından MySQL sunucusuna gönderilmeden önce ayrıştırılmamasını sağlar (olası bir saldırganın kötü amaçlı SQL'i enjekte etme şansı vermez).

Her ne kadar charset kurucu seçeneklerinde, PHP'nin 'eski' sürümlerini not etmek önemlidir (<5.3.6) charset parametresini sessizce yoksaydı DSN'de.

açıklama

Olan şey, size ilettiğiniz SQL ifadesidir. prepare veritabanı sunucusu tarafından ayrıştırılır ve derlenir. Parametreleri belirterek (ya ? veya adlandırılmış bir parametre gibi :name Yukarıdaki örnekte), filtrelemek istediğiniz veritabanı motorunu söylersiniz. Sonra aradığında executeHazırlanan deyim, belirttiğiniz parametre değerleriyle birleştirilir.

Burada önemli olan, parametre değerlerinin bir SQL dizesi değil, derlenmiş ifadeyle birleştirilmesidir. SQL enjeksiyonu, veritabanına göndermek için SQL oluşturduğunda betiğin kötü amaçlı dizeleri içerisine almasını engelleyerek çalışır. Böylece, gerçek SQL'i parametrelerden ayrı olarak göndererek, niyet etmediğiniz bir şeyle sonuçlanma riskini sınırlandırırsınız. Hazırlanan bir deyimi kullanırken gönderdiğiniz tüm parametreler yalnızca dizeler olarak ele alınacaktır (veritabanı altyapısı bazı optimizasyonlar yapabilmesine rağmen, parametreler elbette sayı olarak da olabilir). Yukarıdaki örnekte, eğer $namedeğişken içerir 'Sarah'; DELETE FROM employees sonuç sadece dizge için bir arama olurdu "'Sarah'; DELETE FROM employees"ve sen bitmeyeceksin boş bir masa.

Hazırlanan ifadeleri kullanmanın bir diğer yararı, aynı deyimi aynı oturumda birçok kez çalıştırırsanız, yalnızca bir kez ayrıştırılıp derlenecek ve size biraz hız kazanacaktır.

Oh, ve bir eklenti için nasıl yapılacağını sorduğun için, bir örnek (PDO kullanarak):

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

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

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

Sorgu parametreleri için hazırlanmış deyimleri hala kullanabilirsiniz, ancak dinamik sorgu yapısının kendisi parametrelendirilemez ve belirli sorgu özellikleri parametrize edilemez.

Bu özel senaryolar için 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';
}

7954



Sadece eklemek için başka bir yerde görmedim çünkü başka bir savunma hattı web uygulaması güvenlik duvarı (WAF), sql enjeksiyon saldırılarını aramak için kurallara ayarlanmış olabilir: - jkerak
Ayrıca, mysql_query'nin resmi dokümantasyonu sadece bir sorguyu yürütmeye izin verir, böylece başka herhangi bir sorgu; göz ardı edilir. Bu zaten kullanımdan kaldırılmış olsa bile, PHP 5.5.0 altında çok sayıda sistem var ve bu işlevi kullanabilir. php.net/manual/en/function.mysql-query.php - Randall Valenciano
Bu kötü bir alışkanlıktır ancak problem sonrası bir çözümdür: Sadece eski bir web sitenize veya uygulamanıza sahipseniz, yalnızca SQL enjeksiyonu için değil, herhangi bir enjeksiyon için (örneğin F3 framework v2'de bir görünüm şablonu enjeksiyon deliği vardı) Enjeksiyon kusurlarından bir çözüm, bootstrapte kaçan değerleri olan $ _POST gibi supperglobal önceden tanımlanmış varyantlarınızın değerlerini yeniden atamaktır. PDO tarafından, yine de (bugünün çerçeveleri için) kaçmak mümkündür: substr ($ pdo-> quote ($ str, \ PDO :: PARAM_STR), 1, -1) - Alix
Bu cevabın hazırladığı bir ifadenin ne olduğuyla ilgili bir açıklama yoktur - bir şey - isteğiniz sırasında çok fazla hazırlanmış ifade kullanırsanız ve bazen 10x performans isabeti için geçerliyse bir performans isabetidir. Daha iyi durumda, parametrenin bağlanması kapalıyken PDO, ancak deyim hazırlığı kapalı olacaktır. - donis
PDO kullanmak daha iyidir, doğrudan sorgu kullandığınızda mysqli :: escape_string kullandığınızdan emin olun - Kassem Itani


Uyarı:   Bu yanıtın örnek kodu (sorunun örnek kodu gibi) PHP’nin mysql PHP 5.5.0'da kullanımdan kaldırılmış ve tamamen PHP 7.0.0'da kaldırılmıştır.

PHP'nin yeni bir sürümünü kullanıyorsanız, mysql_real_escape_string aşağıda özetlenen seçenek artık kullanılamayacaktır mysqli::escape_string modern bir eşdeğerdir). Bu günlerde mysql_real_escape_string seçenek sadece eski bir PHP sürümünde eski kod için anlam ifade eder.


İki seçeneğiniz var - özel karakterlerden kaçmak unsafe_variableveya parametreli bir sorgu kullanarak. Her ikisi de sizi SQL enjeksiyonundan koruyacaktı. Parametreli sorgu, daha iyi uygulama olarak kabul edilir, ancak kullanabilmeniz için PHP'de daha yeni bir mysql uzantısına geçmeyi gerektirir.

İlk önce kaçan alt efekt 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, detayları mysql_real_escape_string işlevi.

Parametreli sorguyu kullanmak için kullanmanız gerekir MySQLi Yerine 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();
?>

Orada okumak isteyeceğiniz anahtar işlev mysqli::prepare.

Ayrıca, başkalarının önerdiği gibi, bir şeyle bir soyutlama katmanı oluşturmak için yararlı / kolay bulabilirsiniz. PDO.

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

  • SQL'in yapısını kullanıcı girdisine göre değiştirmek isterseniz, parametreli sorgular yardımcı olmayacaktır ve gerekli çıkış kaçağı tarafından karşılanmayacaktır. mysql_real_escape_string. Bu tür bir durumda, yalnızca 'güvenli' değerlere izin verildiğinden emin olmak için kullanıcının girdisini beyaz listeden geçirmeniz daha iyi olur.
  • Kullanıcı girişinden bir koşulu kullanarak tam sayıları kullanır ve mysql_real_escape_string yaklaşım, tarafından açıklanan sorun muzdarip olacak Polinom Aşağıdaki yorumlarda. Bu durum daha zorlayıcı çünkü tamsayılar tırnak içine alınmayacaktı, bu yüzden kullanıcı girdisinin sadece rakam içerdiğini doğrulayarak başa çıkabilirsiniz.
  • Farkında olmadığım başka durumlar da var. Bulabilirsin bu karşılaşabileceğiniz daha ince problemlerin bazılarında yararlı bir kaynaktır.

1514



kullanma mysql_real_escape_string yeterli mi yoksa parametreli mi kullanmalıyım? - peiman F.
@peimanF. Yerel bir projede bile parametrized sorguları kullanmak için iyi bir uygulama yapın. Parametrized sorgular ile garantili SQL enjeksiyon olmayacak. Ancak, sahte almayı engellemek için verileri sterilize etmeniz gerektiğini unutmayın (ör. HTML kodunu bir metne koyma gibi XSS enjeksiyonu) htmlentitiesÖrneğin - Goufalite
@peimanF. Parametreli sorguları ve değerleri bağlamak için iyi uygulama, ama gerçek kaçış dizesi şimdi için iyi - Richard


Buradaki her cevap, sorunun sadece bir kısmını kapsamaktadır.
Aslında, orada dört Dinamik olarak ekleyebileceğimiz farklı sorgu parçaları:

  • dizi
  • bir sayı
  • bir tanımlayıcı
  • bir sözdizimi anahtar kelimesi.

ve hazırlanan ifadeler sadece 2 tanesini kapsar

Ancak bazen sorgumuzu daha dinamik hale getirmek, operatörleri veya tanımlayıcıları da eklemek zorundayız.
Yani, farklı koruma tekniklerine ihtiyacımız olacak.

Genel olarak, böyle bir koruma yaklaşımı dayanmaktadır beyaz liste. Bu durumda, her dinamik parametre komut dosyanızda kodlanmalı ve o setten seçilmelidir.
Ö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

Bununla birlikte, tanımlayıcıları güvenceye almak için başka bir yol var - kaçmak. Alıntı yaptığınız bir kimlik bilginiz olduğu sürece, onları ikiye katlayarak backtick'lerden kaçabilirsiniz.

Başka bir adım olarak, hazırladığımız ifadelerden bazı yer tutucu (sorgudaki gerçek değeri temsil etmek için bir vekil) kullanma ve başka bir tipte bir yer tutucusu (bir tanımlayıcı yer tutucu) icat etme konusunda gerçekten parlak bir fikir alabiliriz.

Yani, uzun hikayeyi kısaltmak için: tutucudur, değil hazırlanan beyan gümüş mermi olarak düşünülebilir.

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

Yine de, SQL söz dizimi anahtar kelimelerle ilgili bir sorun var. AND, DESC ve böyle) ancak beyaz liste bu durumda tek yaklaşım gibi görünüyor.

Güncelleştirme

SQL enjeksiyon koruması ile ilgili en iyi uygulamalar hakkında genel bir anlaşma olmasına rağmen, hala birçok kötü uygulamalar. Ve bazıları da PHP kullanıcılarının zihnine çok kök salmışlar. Örneğin, bu sayfada (çoğu ziyaretçi tarafından görülmese de) vardır 80'den fazla silinen cevap - Hepsi kötü kalite veya kötü ve eski moda uygulamaları nedeniyle topluluk tarafından kaldırıldı. Daha da kötüsü, bazı kötü cevaplar silinmez, aksine iyileşir.

Örneğin, Orada (1)  , (2)  yine de (3)  Birçok (4)  cevaplar (5), dahil olmak üzere en fazla ikinci cevap manuel dizgeyi öneriyorum - güvensiz olduğu kanıtlanmış modası geçmiş bir yaklaşım.

Ya da sadece öneren biraz daha iyi bir cevap var. başka bir dize biçimlendirme yöntemi ve hatta onu en büyük panacea olarak öne çıkarıyor. Elbette, değil. Bu yöntem, normal dize biçimlendirmeden daha iyi değildir, ancak tüm dezavantajlarını korur: sadece dizeler için geçerlidir ve diğer el ile biçimlendirmelerde, aslında isteğe bağlı, zorunlu olmayan, her türden insan hatasına yatkın bir ölçüdür.

Sanırım tüm bu gibi otoriteler tarafından desteklenen çok eski bir batıl inanç yüzünden OWASP veya PHP kılavuzu“kaçmak” ile SQL enjeksiyonlarından korunma arasındaki eşitliği ilan eder.

PHP kılavuzunun çağlar için söylediklerinden bağımsız olarak, *_escape_string hiçbir şekilde veri güvenli hale getirir ve asla amaçlanmadı. Dize dışındaki herhangi bir SQL parçası için yararsız olmanın yanı sıra, manuel kaçma yanlış çünkü manuel olarak manuel olarak tersi.

Ve OWASP daha da kötüye gidiyor; kullanıcı girişi Bu tam bir saçmalıktır: Enjeksiyon koruması bağlamında böyle bir söz olmamalıdır. Her değişken potansiyel olarak tehlikelidir - kaynak ne olursa olsun! Ya da, başka bir deyişle - her değişken bir sorguya konacak şekilde biçimlendirilmelidir - yine kaynak olursa olsun. Önemli olan hedef. Bir geliştiricinin koyunları keçilerden ayırmaya başladığı an (bazı belirli değişkenlerin “güvenli” olup olmadığını düşünmek) felakete ilk adımını atmaktadır. İfadenin bile, giriş noktasında toplu olarak kaçmayı önerdiğini, çoktan alıntılanmış, çoktan alıntılanmış, kaldırılmış ve kaldırılmış çok özel tırnaklara benzediğini belirtmemek gerekir.

Yani, "kaçma" dan farklı olarak, hazırlanmış ifadeler olduğu gerçekten de gerçekte SQL enjeksiyonundan koruyan önlem (uygulanabilir olduğunda).

Hala ikna olmuyorsan, yazdığım adım adım bir açıklama burada. Otostopçunun SQL Enjeksiyonunu Önleme KılavuzuBütün bu konuları ayrıntılı olarak açıkladığım ve hatta tamamen kötü uygulamalara ve açıklamalarına adanmış bir bölüm hazırladığım yer.


946



Harika, iyi düşünülmüş bir makale. Bunu, PHP'nin Sanitize filtrelerini kullanarak, bir çeşit (ama tam olarak değil) bir çeşit beyaz liste olduğunu ekleyebilirim. Örneğin, FILTER_SANITIZE_NUMBER_INT sadece sayı karakterlerine izin verir, böylece beyaz liste karakterleri, tüm dizeleri değil. Hazırlanan ifadelerle birlikte, iyi bir "kemer ve jartiyer" yaklaşımı yapar. - Sablefoste
@Sablefoste burada beyaz listeye gerek yok. Herhangi bir sterilizasyon gereksiz olacaktır. Takip edecek daha az kural, daha az hata yapacaksınız. Doğrulama yapabilmenize rağmen, uygulama mantığınız için yapın, ancak veritabanı için yapmayın. - Your Common Sense


Kullanmanı tavsiye ederim PDO (PHP Veri Nesneleri) parametreli SQL sorguları çalıştırmak için.

Bu sadece SQL enjeksiyonuna karşı koruma sağlamakla kalmaz, aynı zamanda sorguları hızlandırır.

Ve yerine PDO kullanarak mysql_, mysqli_, ve pgsql_ işlevleri, veritabanı sağlayıcıları geçmek zorunda nadiren ortaya çıkması, veritabanından biraz daha soyut hale getirmek.


770



PDS MySQL DB'ler için mysqli sarmıyor mu? Bu durumda kesinlikle mysqli'den daha hızlı olamaz. Yine de bunu tavsiye ederim. Bu mysqli API çok daha iyi bir arayüz. - Peter Bagnall
Parametreli sorguları kullanmak, sorguları hızlandıran şeydir. Teknik olarak mysqli çok küçük bir marjla daha hızlı olabilir. Sunucunun sorguyu yanıtlamak için harcadığı gerçek zaman miktarı, bir sarıcı kullandığınız için olabilecek zamanlamadaki herhangi bir farkı gizler. Ama mysqli veritabanına bağlı. Farklı bir veritabanı motoru kullanmak istiyorsanız, mysqli kullanan tüm çağrıları değiştirmeniz gerekir. PDO için öyle değil. - Kibbee
PDO dinamik desteklemiyor order by ne yazık ki :( - Horse


kullanım PDO ve hazırlanan sorgular.

($conn bir PDO nesne)

$stmt = $conn->prepare("INSERT INTO tbl VALUES(:id, :name)");
$stmt->bindValue(':id', $id);
$stmt->bindValue(':name', $name);
$stmt->execute();

567



itibaren wikipedia: Hazırlanan ifadeler, SQL enjeksiyonuna karşı dayanıklıdır, çünkü daha sonra farklı bir protokol kullanılarak iletilen parametre değerlerinin doğru şekilde kaçması gerekmez. Özgün deyim şablonu harici girdiden türemediyse, SQL enjeksiyonu yapılamaz. - Imran


Gördüğünüz gibi, insanlar en çok hazırlanmış ifadeleri kullanmanızı önerir. Bu yanlış değil, ancak sorgunuz yürütüldüğünde Sadece bir kere Süreç başına, hafif bir performans cezası olacaktır.

Bu konuyla yüzleşiyordum ama sanırım onu ​​çözdüm çok sofistike bir yol - hackerlar tırnak kullanmaktan kaçınmak için kullanır. Bunu, hazırlanan hazırlanmış ifadelerle birlikte kullanmıştım. Bunu önlemek için kullanıyorum herşey olası olası SQL enjeksiyon saldırıları.

Benim yaklaşımım:

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

  • Tam sayıdan başka bir şey beklerseniz onaltılık. Onaltılıysan, tüm girdiden tamamen kurtulacaksın. C / C ++ 'da bir fonksiyon var mysql_hex_string(), PHP'de kullanabilirsiniz bin2hex().

    Kaçınan dize orijinal boyutunun 2x boyutuna sahip olduğundan endişelenmeyin çünkü kullansanız bile mysql_real_escape_string, PHP aynı kapasiteyi ayırmak zorunda ((2*input_length)+1), aynı olanı.

  • Bu onaltılık yöntem, ikili veri aktarırken sıklıkla kullanılır, ancak SQL enjeksiyon saldırılarını önlemek için neden tüm verilerde kullanılmamasının bir neden göremiyorum. Verileri önceden eklemeniz gerektiğini unutmayın. 0x veya MySQL işlevini kullanın UNHEX yerine.

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 kaçış. Enjeksiyon yolu yok.

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

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

** 0x ** öneki, yalnızca veri sütunları için kullanılabilir. char, varchar, metin, blok, ikili, vb.
Ayrıca boş bir dize eklemek üzereyseniz kullanımı biraz karmaşıktır. Bunu tamamen değiştirmek zorundasınız ''veya bir hata alırsınız.

UNHEX () üzerinde çalışıyor herhangi kolon; boş dize hakkında endişelenmenize gerek yok.


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

Bu hex yönteminin, tamsayılar yalnızca dizgiler gibi olduğu ve yalnızca mysql_real_escape_string. Sonra tırnak kullanımını önleyebilirsiniz.

Örneğin, bunun gibi bir şey yaparsanız:

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

bir saldırı size çok şey enjekte edebilir kolayca. Komut dosyanızdan döndürülen şu enjekte edilen kodu göz önünde bulundurun:

SELECT ... WHERE id = -1 sendika tüm information_schema.tables gelen table_name seçin

ve şimdi sadece tablo yapısını ayıkla:

SELECT ... WHERE id = -1 sendika tüm select column_name from information_schema.column nerede table_name = 0x61727469636c65

Ve sonra sadece istedikleri veriyi seç. Havalı değil mi

Ancak, enjekte edilebilir bir sitenin kodlayıcısı onaltılık bir heceyi yaparsa, sorgu şöyle görünebilirdi, çünkü sorgu şöyle görünecekti: SELECT ... WHERE id = UNHEX('2d312075...3635')


501



@SumitGupta Yea, sen yaptın. MySQL ile bitmez + fakat CONCAT. Ve performansa: Performansı etkilediğini düşünmüyorum çünkü mysql veriyi ayrıştırmak zorundadır ve eğer kök dizge veya altı ise önemli değil - Zaffy
@YourCommonSense Hangi hatalarla karşılaşıyorsunuz? Açık ol. - Zaffy
@YourCommonSense Kavramını anlamıyorsunuz ... Eğer mysql dizgisine sahip olmak isterseniz 'root' ya da onaltılık yapabilirsiniz 0x726f6f74 ANCAK Eğer bir sayı istiyorsanız ve dizge olarak gönderirseniz muhtemelen '42' yazacaksınız CHAR (42) '...' 42 ' 0x3432 değil 0x42 - Zaffy
@YourCommonSense Söyleyecek hiçbir şeyim yok ... sadece lol ... Eğer sayısal alanları onaltmak istiyorsanız hala, ikinci yoruma bakın. Seninle oynayacağına bahse girerim. - Zaffy
@YourCommonSense hala anlamadınız mı? 0x ve concat kullanamazsınız, çünkü dize boşsa bir hatayla biteceksiniz. Sorunuza basit bir alternatif istiyorsanız bunu deneyin SELECT title FROM article WHERE id = UNHEX(' . bin2hex($_GET["id"]) . ') - Zaffy


ÖNEMLİ

SQL Injection'ı önlemenin en iyi yolu kullanmaktır Hazırlanmış İfadeler  kaçmak yerine, gibi kabul edilen cevap göstermektedir.

Gibi kütüphaneler var Aura.Sql ve EasyDB Bu, geliştiricilerin hazırlanmış ifadeleri daha kolay kullanmasına olanak tanır. Hazırlanan ifadelerin neden daha iyi olduğu hakkında daha fazla bilgi edinmek için SQL enjeksiyonunu durdurma, bakın bu mysql_real_escape_string() kalp ameliyati ve WordPress'te son zamanlarda sabit Unicode SQL Injection güvenlik açıkları.

Enjeksiyon önleme - mysql_real_escape_string ()

PHP'nin bu saldırıları önlemek için özel olarak yapılmış bir işlevi vardır. Tek yapman gereken, bir fonksiyonun ağzını kullanmak. mysql_real_escape_string.

mysql_real_escape_string MySQL sorgusunda kullanılacak bir dizgiyi alır ve aynı dizeyi güvenli bir şekilde kaçan tüm SQL enjeksiyon girişimleriyle döndürür. Temel olarak, bir kullanıcının bir MySQL güvenli yerine, kaçan bir alıntıyla girebileceği sorunlu tırnakların (') yerini alacaktır.

NOT: Bu işlevi kullanmak için veritabanına bağlı olmalısınız!

// MySQL'e bağlanma

$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;

Daha fazla ayrıntı bulabilirsiniz MySQL - SQL Enjeksiyon Önleme.


455



Bu eski mysql uzantısı ile yapabileceğiniz en iyisidir. Yeni kod için mysqli veya PDO'ya geçmeniz tavsiye edilir. - Álvaro González
Bu 'bu saldırıları önlemek için özel olarak yapılmış bir işlev' ile aynı fikirdeyim. bence mysql_real_escape_string amaç, her giriş veri dizesi için doğru SQL sorgusu oluşturmaya izin vermektir. Önleme sql-injection, bu işlevin yan etkisidir. - sectus
Doğru giriş veri dizelerini yazmak için işlev kullanmazsınız. Sadece kaçmak istemeyen veya çoktan kaçmış doğru olanları yaz. mysql_real_escape_string () aklında bahsettiğiniz amaç ile tasarlanmış olabilir, ancak tek değeri enjeksiyonun önlenmesidir. - Nazca
UYARI!  mysql_real_escape_string()  yanılmaz değil. - eggyal
mysql_real_escape_string şimdi kullanımdan kaldırılmıştır, bu yüzden artık geçerli bir seçenek değildir. Gelecekte PHP'den kaldırılacak. PHP veya MySQL üyelerinin önerdiği şeylere geçmek en iyisidir. - jww


Güvenlik uyarısı: Bu cevap güvenlikle ilgili en iyi uygulamalarla uyumlu değil. Kaçma, SQL enjeksiyonunu önlemek için yetersiz, kullan hazırlanan ifadeler yerine. Aşağıda belirtilen stratejiyi kendi sorumluluğunuzda kullanın. (Ayrıca, mysql_real_escape_string() PHP 7'de kaldırılmıştır.)

Bunun gibi temel bir şey yapabilirsin:

$safe_variable = mysql_real_escape_string($_POST["user-input"]);
mysql_query("INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

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


413



Örneğini denedim ve benim için iyi çalışıyor. "Bu her problemi çözmeyecek" diyebilir misin? - Chinook
Dizgeyi alıntılamazsanız, hala enjekte edilebilir. almak $q = "SELECT col FROM tbl WHERE x = $safe_var"; Örneğin. Ayar $safe_var için 1 UNION SELECT password FROM users Bu durumda tırnakların eksikliği nedeniyle çalışır. Kullanarak sorgu içine dizeleri enjekte etmek de mümkündür CONCAT ve CHR. - Polynomial
@Polynomial Tamamen doğru, ama bunu sadece yanlış kullanım olarak görüyorum. Doğru şekilde kullandığınız sürece kesinlikle işe yarayacaktır. - glglgl
UYARI!  mysql_real_escape_string()  yanılmaz değil. - eggyal
mysql_real_escape_string şimdi kullanımdan kaldırılmıştır, bu yüzden artık geçerli bir seçenek değildir. Gelecekte PHP'den kaldırılacak. PHP veya MySQL üyelerinin önerdiği şeylere geçmek en iyisidir. - jww


Kullandığınız şey ne olursa olsun, girişinizin önceden karıştırılmadığını kontrol ettiğinizden emin olun. magic_quotes ya da başka iyi anlamdaki çöpler, ve gerekirse stripslashes ya da her ne şekilde olursa olsun.


347



Aslında; açık olan magic_quotes ile çalıştırılması, yalnızca kötü uygulamaları teşvik eder. Bununla birlikte, bazen ortamı her zaman bu seviyeye kadar kontrol edemezsiniz - ya sunucuyu yönetmek için erişiminiz yoksa ya da uygulamanızın (shudder) bu yapılandırmaya bağlı uygulamalarla bir arada olması gerekir. Bu nedenlerden dolayı, taşınabilir uygulamalar yazmak iyidir - açık olsa da, dağıtım ortamını kontrol ederseniz, çaba harcanır, örn. çünkü bu bir şirket içi uygulama ya da sadece belirli bir ortamda kullanılacaktır. - Rob
PHP 5.4'ten itibaren 'sihirli alıntılar' olarak bilinen iğrenç ölü öldürüldü. Ve kötü çöplere karşı iyi bir kurtuluş. - BryanH


Parametreli sorgu VE giriş doğrulama, gitmenin yoludur. SQL enjeksiyonunun gerçekleşebileceği birçok senaryo vardır. mysql_real_escape_string() kullanıldı.

Bu örnekler SQL enjeksiyonuna karşı savunmasızdır:

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

veya

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

Her iki durumda da kullanamazsınız ' kapsüllemeyi korumak için.

Kaynak: Beklenmeyen SQL Enjeksiyonu (Kaçarken Yeter Değil)


330



Kullanıcı girdisinin, uzunluk, tür ve sözdizimi için tanımlanmış kurallar kümesine ve ayrıca iş kurallarına aykırı olduğu bir giriş doğrulama tekniğini kabul ederseniz SQL enjeksiyonunu engelleyebilirsiniz. - Josip Ivic