Php ile SQL ve XSS güvenliği nasıl sağlanır?
Bu yazımda php ile sql ve xss güvenliği nasıl sağlanır, açıkların mantığı nedir bundan bahsedicem.
Öncelikle günümüzde de yaygın olan sql ve xss zaafiyetleri halen yeni yazılımcıların sistemlerinde veya güncel türemiş açıklar ile güncel sistemlerde bulunuyor. Bu açıklar artık saldırganların otomasyon hazırladığı zararlı yazılımlar ile otomatik deneniyor ve sisteminizde bulunuyor ise saldırganın tespit etme olanağı %80 orandadır.
SQL injection nedir? nasıl çalışır ve nasıl önlenir?
Sql injection veritabanına gönderilen sorguların saldırganlar tarafından girilen veri ile sabote edilip sorgunun değiştirilmesidir. Yani siz şu tabloda şu sütunu ara şeklinde bir sorgu yazdığınızda aranan veriyi saldırgan giriyor ise o sorguyu değiştirebilir.
Basit bir örnek ile:
$saldirganin_verisi = "'or kullanici_tipi = 'yonetici'";
$sorgu = "SELECT * FROM kullanicilar WHERE isim = '$saldirganin_verisi'"
Yukarıda bulunan sorgu üzerinde kullanıcılar tablosunda isim sütun’u arandı. Fakat saldırgan sorguyu bozarak tırnak işareti ile kapatıp “veya kullanıcı tipi yönetici olan kullanıcının bilgilerini getir” dedi ve yönetici bilgileri ile işleme devam etti.
Bu nasıl bir tehlike yaratıyor?
Saldırgan eğer sorguyu bozup kullanıcı tipi yönetici olan kişileri listeler ise yönetici bilgilerini(e-posta, parola, isim ve soyisim vb.) çalabilir. Bunları değiştirebilir ve düzenleyebilir.
Bu sql injection açığı mysql ve mysqli kullanılan sistemlerde yaygındır. PDO ile bunların önüne geçilmiştir fakat pdo üzerinde bazı sorgu türlerinde hala açık mevcuttur.
MySQL ve MySQLi üzerinde korumayı nasıl sağlarım?
Tavsiyem mysql veya mysqli kullanmamanız yönünde fakat koruma sağlamak istiyorsanız kullanıcının girdiği verileri sınırlamalısınız. Örnek olarak:
$saldirganin_verisi = "'or kullanici_tipi = 'yonetici'";
if(preg_match("/[-]{2,}|[;]|[']|[\*]/", $saldirganin_verisi)) {
echo "Zararlı veriler tespit edildi.";
}
PDO ile sql injection güvenliği
PDO kullanarak sql injection açığını önleyebiliriz fakat query ve exec metotları yerine prepare kullanmamız gerekiyor. Çünkü query ve exec metotları mysql ve mysqli’den farksız şekilde sorgu yapıyor.
query, exec kullanırken verileri korumak istersek quote fonksiyonu ile sorgudan geçirmemiz gerekiyor. Quote fonksiyonu girdiğiniz verinin sayısal veya metinsel olarak ikiye ayırıyor id verilerine integer metin verilerine string şeklinde belirtip fonksiyonu kullanabiliyoruz.
Örnek verecek olursam:
// bağlantı sağlayalım
$leventemreconn = new PDO("mysql:host=localhost;dbname=sistem", "veritabaniuser", "veritabaniparola");
$leventemreconn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// $_GET["id"] üzerinden gelen verimizi quote ile sayısal veri olarak kontrol edelim.
$idveri = $_GET["id"];
$idveri = $leventemreconn->quote($idveri, PDO::PARAM_INT);
// $_GET["hakkinda"] üzerinden gelen verimizi quote ile metinsel veri olarak kontrol edelim.
$hakkindaveri = $_GET["hakkinda"];
$hakkindaveri = $leventemreconn->quote($hakkindaveri, PDO::PARAM_STR);
$guncelle = $leventemreconn->exec("UPDATE uyeler SET hakkinda = $hakkindaveri WHERE id = $idveri");
Yukarıda bulunan sorgu üzerinde id olarak gelen veriler integer yani sayısal olarak belirtildi, hakkında olarak gelen veriler ise string yani metinsel olarak belirtilip sorgudan geçirilmiştir.
PDO’nun en güvenli sorgu türü: Prepare
PDO kullanacaksanız yukarıda anlattığım sorgu türlerini bir kenara bırakıp tamamen güvenli şekilde ilerleyen prepare fonksiyonu ile ilerleyelim.
Prepare kullanırken “bindParam” fonksiyonunu kullanacağız. “bindParam” fonksiyonu quote ile aynı işlemi görüyor ve güvenli şekilde veriyi sorguya yerleştiriyor. Prepare bindParam, bindValue tarzı metotları ile beraber çalışır ve verileri sorguya güvenli şekilde yerleştirmemizi sağlar.
bindParam metot örneği:
$id = $_GET["id"];
$uyenick = $_GET["uyenick"];
//Prepare ile sorgumuz içerisinde yerleştireceğimiz bindParam verilerini :veri şeklinde belirtiyoruz ardından bindParam ile veriyi giriyoruz ve veri türünü belirliyoruz.
$arama = $leventemreconn->prepare("SELECT * FROM uyeler where uyenick = :uyenick and id = :id");
$arama->bindParam(":uyenick", $uyenick, PDO::PARAM_STR);
$arama->bindParam(":id", $id, PDO::PARAM_INT);
$arama->fetchAll(PDO::FETCH_ASSOC);
$arama->execute();
//Eğer arama sonucu var ise
if($arama->rowCount()) {
//verileri yazdıralım
foreach($arama as $gelenveri) {
echo $gelenveri["uyeadi"]."";
}
}
Yukarıda gösterdiğim php kodunda $uyenick
değişkeni metinsel, $id
değişkeni ise sayısal olarak prepare sorgusuna güvenli şekilde yerleştirilmiştir. Eğer saldırgan $id
değişkenine metinsel bir veri girerse pdo sorgusu hata verecek ve çıktı vermeyecektir.
bindParam INSERT örneği
$uyeadi = $_GET["uyeadi"];
$uyenick = $_GET["uyenick"];
$uyetip = "normal";
$uyesitesi = "leventemre.com";
$ekle = $leventemreconn->prepare("INSERT INTO uyeler(uyeadi, uyenick, uyetip, uyesitesi) VALUES (:uyeadi, :uyenick, :uyetip, :uyesitesi)");
$ekle->bindParam(":uyeadi", $uyeadi, PDO::PARAM_STR);
$ekle->bindParam(":uyenick", $uyenick, PDO::PARAM_STR);
$ekle->bindParam(":uyesitesi", $uyesitesi, PDO::PARAM_STR);
// hata var ise yazdır
$ekle->execute($array) or print_r($ekle->errorInfo(), true);
Sisteminizde bulunan sql injection güvenlik açığınızı kapattırmak isterseniz iletişim sayfası üzerinden benimle iletişim kurabilirsiniz
Xss açığı nasıl önlenir?
Xss açığı saldırganların uzaktan sitenize gönderdiği verilerin site üzerinde HTML veya Javascript olarak çalıştırarak uyguladığı bir güvenlik açığıdır. Engellenmez ise kullanıcıların oturumları çalınabilir veya site saldırganlar tarafından kapatılabilir.
Xss açığını php üzerinde htmlspecialchars
ve htmlentities
fonksiyonları ile önleyebiliriz. Örnek olarak:
$saldirganin_verisi = "<h1>leventemre.com</h1>";
$htmltaglarinidegistir = htmlspecialchars($saldirganin_verisi);
echo $htmltaglarinidegistir;
Yukarıda uyguladığım htmlspecialchars
fonksiyonu tagları öğe haline getirerek sayfada html olarak çalışmasını engeller. Fakat htmlspecialchars
fonksiyonunun belirli dönüşümleri vardır örneğin tırnak işareti öğe haline getirilmezse input
veya a
tagları içerisinde güvenlik açığı meydana gelir:
$saldirganin_verisi = "' onclick='alert(1)'";
$saldirganin_verisi = htmlspecialchars($saldirganin_verisi);
$html = "<input value='$saldirganin_verisi'>";
echo $html;
Yukarıda bulunan kodu herhangi bir yerde denediğinizde htmlspecialchars
fonksiyonun dönüşüm belirtilmediği halde tek başına saldırganı engelleyemediğini anlayabilirsiniz. Engellemek için ENT_QUOTES
dönüşümünü belirterek tırnaklarıda öğeye çevirelim:
$saldirganin_verisi = "' onclick='alert(1)'";
$saldirganin_verisi = htmlspecialchars($saldirganin_verisi, ENT_QUOTES);
$html = "<input value='$saldirganin_verisi'>";
echo $html;
ENT_QUOTES
dönüşümünü kullandıktan sonra açığı engelledik. Umarım bu yazı sizin için faydalı olmuştur.