Kullanıcı girdisi değiştirilmeden bir SQL sorgusuna eklenirse, uygulama aşağıdaki örnekte olduğu gibi SQL enjeksiyonuna karşı savunmasız hale gelir:
$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 girebilmesi ve sorgunun bu hale gelmesidir:
INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')
Bunun olmasını önlemek için ne yapılabilir?
Deprecated Warning: Bu yanıtın örnek kodu (sorunun örnek kodu gibi) PHP 5.5.0'da kullanımdan kaldırılan ve PHP 7.0.0'da tamamen kaldırılan PHP'nin `MySQL' eklentisini kullanmaktadır.
Güvenlik Uyarısı: Bu cevap en iyi güvenlik uygulamaları ile uyumlu değildir. Kaçış SQL enjeksiyonunu önlemek için yetersizdir, bunun yerine hazırlanmış ifadeler kullanın. Aşağıda özetlenen stratejiyi kendi sorumluluğunuzda kullanın. (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 özetlenen mysql_real_escape_string
seçeneği artık kullanılamayacaktır (ancak mysqli::escape_string
modern bir eşdeğerdir). Bugünlerde mysql_real_escape_string
seçeneği yalnızca PHP'nin eski bir sürümündeki eski kodlar için mantıklı olacaktır.
İki seçeneğiniz var: unsafe_variable
içindeki özel karakterlerden kaçmak ya da parametrelendirilmiş bir sorgu kullanmak. Her ikisi de sizi SQL enjeksiyonundan koruyacaktır. Parametrelendirilmiş sorgu daha iyi bir uygulama olarak kabul edilir, ancak kullanabilmeniz için PHP'de daha yeni bir MySQL eklentisine geçmeniz gerekecektir.
Önce daha düşük etkili ip kaçışını 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 da bakın.
Parametrelendirilmiş sorguyu kullanmak için MySQL işlevleri yerine MySQLi işlevlerini kullanmanız gerekir. Örneğinizi yeniden yazmak için aşağıdaki gibi bir şeye ihtiyacımız olacaktır.
<?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();
?>
Burada okumak isteyeceğiniz anahtar fonksiyon mysqli::prepare
olacaktır.
Ayrıca, diğerlerinin de önerdiği gibi, PDO gibi bir şeyle bir soyutlama katmanı yükseltmeyi daha yararlı/kolay bulabilirsiniz.
Lütfen sorduğunuz vakanın oldukça basit bir vaka olduğunu ve daha karmaşık vakaların daha karmaşık yaklaşımlar gerektirebileceğini unutmayın. Özellikle de:
mysql_real_escape_string
tarafından kapsanmamaktadır. Bu tür bir durumda, yalnızca 'güvenli' değerlere izin verildiğinden emin olmak için kullanıcı girdisini bir beyaz listeden geçirmeniz daha iyi olacaktır.mysql_real_escape_string
yaklaşımını benimserseniz, aşağıdaki yorumlarda Polynomial tarafından açıklanan sorundan muzdarip olursunuz. Bu durum daha zordur çünkü tamsayılar tırnak işaretleriyle çevrelenmeyecektir, bu nedenle kullanıcı girdisinin yalnızca rakam içerdiğini doğrulayarak başa çıkabilirsiniz.Parametrelendirilmiş SQL sorgularını çalıştırmak için PDO (PHP Data Objects) kullanmanızı tavsiye ederim.
Bu sadece SQL enjeksiyonuna karşı koruma sağlamakla kalmaz, aynı zamanda sorguları da hızlandırır.
Ve mysql_
, mysqli_
ve pgsql_
fonksiyonları yerine PDO kullanarak, veritabanı sağlayıcılarını değiştirmeniz gereken nadir durumlarda uygulamanızı veritabanından biraz daha soyutlamış olursunuz.
Bunun gibi basit bir şey yapabilirsiniz:
$safe_variable = mysqli_real_escape_string($_POST["user-input"], $dbConnection);
mysqli_query($dbConnection, "INSERT INTO table (column) VALUES ('" . $safe_variable . "')");
Bu her sorunu çözmeyecektir, ancak çok iyi bir atlama taşıdır. Değişkenin varlığını, biçimini (sayılar, harfler, vb.) kontrol etmek gibi bariz öğeleri dışarıda bıraktım.