Zihni Özgürlük

Güvenli PHP Uygulamaları – Formların Güvenliği

Betiklerimize istemediğimiz yerlerden form verilerinin gönderilmesiyle ortaya çıkar.

Aşağıdaki gibi bir formumuz(form.php) olsun:

<form action="formIsle.php" method="POST">
	<input type="radio" name="medeniHali" value="bekar" checked="checked">
	<input type="radio" name="medeniHali" value="evli">
	<input type="radio" name="medeniHali" value="dul">
	<input type="submit" value="Gönder" />
</form>

Bu formu işleyen sayfamız(formIsle.php) ise aşağıdaki gibi olsun, formdan gelen medeni hali bilgisini yazdırıyor.

1
2
3
4
<?php
echo $_POST['medeniHali'];       
// Çıktı: bekar
?>

Saldırganın aşağıdaki formu(form.php) kendi bilgisayarından gönderdiğini varsayalım. Formun action özelliğine tam adresi yazıyor.

1
2
3
4
<form action="http://www.siteadresi.com/formIsle.php" method="POST">
	<input type="text" name="medeniHali" value="yalancı">
	<input type="submit" value="Gönder" />
</form>

Form işleyen(formIsle.php) sayfamız yukarıda gönderilen formu işler.

1
2
3
4
<?php
echo $_POST['medeniHali'];
// Çıktı: yalancı
?>

Medeni hali, bekar-evli-dul değerlerinden birini alması gerekirken, “yalancı” diye bir değer almıştır.

Bunu önlemek için tek kullanımlık bir şifre oluşturulabilir:

1
2
3
4
5
6
7
8
9
10
11
<?php 
$formAnahtar = rand(10000,1000000);
$_SESSION['formAnahtar'] = $formAnahtar;
?>
<form action="http://www.siteadresi.com/formIsle.php" method="POST">
	<input type="radio" name="medeniHali" value="bekar" checked="checked">
	<input type="radio" name="medeniHali" value="evli">
	<input type="radio" name="medeniHali" value="dul">
	<input type="hidden" name="formAnahtar" value="<?php echo $formAnahtar; ?>">
	<input type="submit" value="Gönder" />
</form>

Formu işleyen(formIsle.php) sayfamız:

1
2
3
4
5
6
7
<?php
 
if($_SESSION['formAnahtar'] == $_POST['formAnahtar'])
	echo $_POST['medeniHali'];
else 
	echo "Bu form başka bir yerden geliyor.";
?>

Güvenli PHP Uygulamaları – Cross site scripting(XSS)

Kullanıcının betiklerimize html kodu enjekte etmesiyle ortaya çıkar.
Girdi filtrelenmeden çıktı olarak sunuluyorsa bu saldıya uğranabilir.

Aşağıdaki gibi bir formumuz olsun:

1
2
3
4
<form id="form1" action="" method="post">
<textarea name="mesaj"></textarea>
<input type="submit" value="gönder" />
</form>

Formdaki mesaj alanına aşağıdaki javascript kodunun enjekte edildiğini varsayalım.

1
2
3
<?php 
<script type="text/javascript">window.location = 'http://www.zihni.net' </script>
?>

Girdiyi filtrelemeden, aşağıdaki şekilde ekrana yazdırırsak, sayfaya giren her kullanıcıyı istediğimiz siteye yönlendiririz.

1
2
3
<?php 
echo $_POST['mesaj'];
?>

Yukardaki kodun zararlı etkilerinden korunmak için girdiyi htmlentities() işleviyle filtrelememiz gerekir.

1
2
3
<?php 
echo htmlentities($_POST['mesaj']);
?>

Güvenli PHP Uygulamaları – Kullanıcı Girdilerinin Filtrelenmesi

Kullanıcı girdilerine güvenilmemeli, betiklerimizin güvenliği için
tüm girdiler filtrelenmelidir.

Örneğin kullanıcıdan yaş bilgisini isteyelim:

Kullanıcıdan beklenen girdi, sayı türünde bir değerdir.
Aşağıdaki kod bu doğrulamayı yapar.

$sonuc = preg_match("/[0-9]{1,3}/",$_POST['yas']);
if($sonuc === true)
echo "Girdi doğrulandı";
else
echo "Girdi sayı değil";

Kullanıcının hayat hikayesini istediğimizi varsayalım, yukardaki gibi bir basit bir doğrulama yapamayız, bunun yerine php nin bize sunduğu temel filtreleme işlevlerinden yararlanabiliriz.
Html tagları sorunlara yol açabilir.
stript_tags() işlevi html etiketlerini yok eder.

$girdi = "<h1>büyük bi başlık</h1><span>span etiketi</span>";
echo strip_tags($girdi);

Yukardaki örneğin çıktısı:

büyük bi başlık span etiketi

Bazı etiketlere izin vermek istiyorsak:

$girdi = "<h1>büyük bi başlık</h1><span>span etiketi</span>";
echo strip_tags($girdi,"<span>");</span>

Yukardaki örneğin çıktısı:

büyük bi başlık <span>span etiketi</span>

htmlentities() işlevi tüm html karakterlerini html entitilerine çevirir.

$girdi = "<h1>büyük bi başlık</h1>";
echo htmlentities($girdi, ENT_QUOTES,'UTF-8');

yukarıdaki örneğin çıktısı:

&lt;h1&gt;büyük bi başlık&lt;h1&gt;

Javascript Hatası:…submit is not a function

Javascripteki ayrılmış kelimeler(reserved words), button ismi ve fonksiyon olarak kullanıldığında çatışma(collision) meydana geliyor.

Hata Mesajı:

1
this.form.submit is not a function

“Submit” metodunu kullandığım yer:

1
<select  onchange="this.form.submit();">

Buton adı olarak “submit” kulladığım yer:

1
<input type="submit" name="submit" value="Ekle">

Buton adını “submit”ten başka bir adla, örneğin “ekle” olarak değiştirdiğimde sorun düzeliyor.

1
<input type="submit" name="ekle"  value="Ekle">

Güvenli PHP Uygulamaları-Sql enjektelerine karşı önlemler

Veritabanına gönderilen her bir değişken mysql_real_escape_string()
işlevinden geçirilerek sql enjeksiyonlarına karşı önlem alınabilir.
Bu işlev veritabanları için özel anlamı olan karakterleri escape-kurtarma işlemine tabii tutar,
bu karakterlerden bazıları: \n, \r, \, ‘, ”

Aşağıdaki gibi kullanıcı adı ve şifre alanlarından oluşan bir giriş formumuz olsun:

1
2
3
4
5
6
7
8
9
10
11
12
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>mysql_real_escape_string</title>
</head>
<body>
<form name="form1" action="" method="post">
Kullanıcı adı:<input type="text" name="kullanici_adi" value="" /> <br />
Şifre<input type="password" name="sifre" value="" /> <br />
<input type="submit" value="gönder" name="gonder" /></div>
</body>
</html>

Kullanıcının formdan aşağıdaki bilgileri gönderdiğini varsayalım.

1
2
$_POST['kullanici_adi'] = 'zihni';
$_POST['sifre'] = "' OR ''='";

Kullanıci adi ve sifre verilerinin veritabanında olup olmadığını kontrol edelim

1
2
3
4
5
6
7
8
<?php
$sorgu = "SELECT * FROM kullanicilar
WHERE kullanici_adi='{$_POST['kullanici_adi']}' AND sifre='{$_POST['sifre']}'";
 
$sonuc = mysql_query($sorgu) or die(mysql_error());
$satir = mysql_fetch_array($sonuc);
echo $satir['kullanici_adi'];
?>

mysql’e gönderilen sorgu aşağıdaki gibi olur:

1
SELECT * FROM kullanicilar WHERE kullanici_adi='zihni' AND sifre='' OR ''=''

Görüldüğü gibi sifre geçersiz olsa bile sorgu sonucu başarılı olur.

Eğer formdan gelen bilgileri mysql_real_escape_string() işlevinden geçirdikten sonra veritabanına gönderirsek:

1
2
3
4
5
6
7
8
9
10
11
<?php
$sorgu = sprintf("SELECT * FROM kullanicilar
WHERE kullanici_adi='%s' AND sifre='%s'",
mysql_real_escape_string($_POST['kullanici_adi']),
mysql_real_escape_string($_POST['sifre']) );
 
$sonuc = mysql_query($sorgu) or die(mysql_error());
$satir = mysql_fetch_array($sonuc);
echo $satir['kullanici_adi'];
echo $sorgu;
?>

mysql’e gönderilen sorgu aşağıdaki gibi olur ve bir sonuç döndürmez.

1
SELECT * FROM kullanicilar WHERE kullanici_adi='zihni' AND sifre='\' OR \'\'=\''