Php Input Validation
PHP INPUT VALIDATION
Elbette kullanıcıdan gelen veriyi doğrulamak (validate) dinamik web uygulamaları için bir oldukça önemlidir. Geçerli olmayan kullanıcı inputları hatalara neden olabilir. Bu yüzden input validation bir zorunluluktur. PHP input validation için çeşitli fonksiyonları built-in olarak bize sağlar.
Bu yazıda 4 konuyu işleyeceğiz
- Request metodunu doğrulama
- Kullancı inputunu kontrol etme
- İnputu doğrulama (validate)
- İnput validation için kendı sınıfını oluşturma
Request Metodunu Doğrulama
Html formunu oluşturmadan önce formda kullanmak istediğiniz http metoduna karar vermelisiniz. Birçok http metodu olmasına rağmen genellikle GET
ve POST
metodları formlar için daha sık kullanılır. Sonuç olarak GET
mi POST
mu ???
GET Metodu
- Kaynaktan bir şey talep etmek için kullanılır.
- Anahtar değer şeklinde verileri URL içinde gönderir.
- Kaynaktan veri almak için kullanılır.
- Hassas verileri göndermek için kullanılmamalıdır.
POST
metoduna göre daha az güvenlidir.- Farklı veriler farklı URL oluşturur
- Tarayıcı tarafından cache edilebilir.
- Tarayıcı geçmişinde kalır.
- Uzunluk limiti vardır.
- Sadece ASCII veri gönderilerbilir
POSR Metodu
- Server'a veri göndermek için kullanılır.
- Hassas verileri göndermek için kullanılır.
GET
metoduna göre daha yavaştır.- Veriyi http requestinin body kısmında gönderir.
- Tarayıcı geçmişinde kalmaz.
- Uzunluk/boyut kısıtlaması yoktur.
- Herhangi bir veri gönderilebilir.
Kredi kartı, parola gibi hassas bilgiler gönderiyorsanız metodunuz POST
olmalı. Eğer serverdan bir veri alıyorsanız kullandığınız metod GET
olmalı.
Birtane html formu oluşturalım. Ben POST
metodunu seçtim ve action kısmına action.php
scriptini verdim.
<form method="POST" action="action.php">
<!-- form elemanları buraya -->
</form>
Tarayıcı formu submit edince form datasını action.php
sayfasına gönderecek. Ben action.php
scriptini şu şekilde oluşturdum. Önce http metodunu kontrol ediyor:
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST')
{
// request metodu geçerli
}
else
{
exit('Request metodu geçersiz!!!');
}
Yada daha kısa olarak şu şekilde:
<?php
if ($_SERVER['REQUEST_METHOD'] !== 'POST')
{
exit('Request metodu geçersiz!!!');
}
if
kontrolü içinde istediğiniz http metodunu kontrol edebilirsiniz. Bu örnekte action.php
sadece POST
metodunu kontrol edecek. Eğer metod POST
değilse bir hata gösterecek. Http metod kontrolü basit bir şekilde böyle yapılabilir.
Kullanıcı Verisini Kontrol Etme
Bir hata olmaması için kullanıcı verisini kontrol etmeliyiz. Bu hatanın nasıl oluştuğuna bakalım. Bir php kodunuz var POST
metoduyla gelen değeri local değişkene atıyor:
$email = $_POST['email'];
Eğer "email" post metodu ile set edilmediyse, php bir hata üretecek: Undefined Index: Email
Bu yüzden input değişkeninin olup olmadığını kontrol etmeliyiz.
Boolean Olmayan İnputlar
PHP deki empty()
fonksiyonu bu durum için oldukça önemlidir. Bu fonksiyon argümanın boş olup olmadığını kontrol eder. Aşağıdaki durumlarda TRUE
değerini döner:
""
boş string0
integer olarak 00.0
float olarak- "0" string olarak
NULL
- "FALSE`
array()
boş bir array
if( ! empty($_POST['email']))
{
$email = $_POST['email'];
}
Yukarıdaki örnekte $_POST["email"]
boş değilse $email
değişkeni tanımlanır ve değeri değişkene atanır. Bir önceki hata ile karşılaşmayız. Eğer email alanı zorunlu ise bir hata üretebiliriz. Buna daha sonra değineceğiz.
Boolean İnputlar
Boolean değişlenler için isset()
fonksiyonu kullanılabilir. isset()
fonksiyonu bir değişkenin tanımlandığını (declerated) ve değerinin NULL
olmadığı durumlarda TRUE
döner.
if (isset($_POST['boolean']))
{
$boolean = $_POST['boolean'];
}
İnputları Doğrulama
XSS Saldırılarından Korunma
Öncelikle XSS saldırılarından korunmamız gerekiyor. Aşağıdaki forma kullanıcıların kullanıcı adlarını yazmaları için bir alan olduğunu düşünün.
<form method="POST" action="">
<input type="text" name="username">
<input type="submit" name="submit">
</form>
Bir saldırganın kullanıcı adı olarak şöyle bir js kodu girdiğini düşünün
<script>location.href='https://www.saldirgansite.com'</script>
Ve sonra formu submit etti. Bizde bir validation uygulamadan veritabanına kaydettik. Daha sonra kullanıcıların kullanıdıadlarını listelediğimiz bir sayfa hazırladık. Birde saldırganın gönderdiği kullanıcı adını yazdırdığımızda, herkim sayfamızı zirayet ederse saldırganın sitesine bizi yönlendirecek. (https://www.saldirgansite.com). İşte basit tabiriyle XSS böyle bir şey.
Web uygulamamızı XSS saldırılarından korumak oldukça basit. htmlspecialchars()
fonksiyonu html deki kaçış karakterlerini temizler. Saldırganın girdiği input:
<script>location.href='https://www.attacker.com'</script>
Şu hale dönüşür:
<script>location.href='https://www.attacker.com'</script>
Bir örneğe bakalım.
if (!empty($_POST['username']) && !empty($_POST['email']))
{
$username = htmlspecialchars($_POST['username']);
$email = htmlspecialchars($_POST['email']);
}
Kullanım yukarıdaki şekilde. Yapmamız gereken bir şey daha var. Kullanıcı inputundan gelen fazla boşlukları silmeliyiz. Böylece veritabanımızda fazladan yer kaplamazlar. PHP deki trim()
fonksiyonu burada kullanılabilir.
if ( ! empty($_POST['username']) && ! empty($_POST['email']))
{
$username = trim(htmlspecialchars($_POST['username']));
$email = trim(htmlspecialchars($_POST['email']));
}
Email URL Integer Doğrulama
PHP değişkenleri doğrulamak için filter_var()
fonksiyonunu bize sunar. Fonksiyondaki 2. parametreyi çeşitli değerleri doğrulamak için kullanabiliriz. Bu fonksiyon bir hata olduğunda yada input geçersiz olduğunda FALSE
döner.
Email Doğrulama
Basit bir şekilde email doğrulamak için filter_var()
fonksiyonunun 2. parametresine FILTER_VALIDATE_EMAIL
değerini verebiliriz. Eğer email geçerli değilse FALSE
döner.
if ( ! empty($_POST['email']))
{
$email = trim(htmlspecialchars($_POST['email']));
$email = filter_var($email, FILTER_VALIDATE_EMAIL);
if ($email === FALSE)
{
exit("Email Geçerli Değil !!!");
}
}
URL Doğrulama
FILTER_VALIDATE_URL
flagi ile URL doğrulamasını filter_var()
fonksiyonu ile yapabiliriz. Eğer URL doğru formatta değilse FALSE
döner, doğru formatta ise parametre olarak verdiğimiz URL döner.
if ( ! empty($_POST['url']))
{
$url = trim(htmlspecialchars($_POST['url']));
$url = filter_var($url, FILTER_VALIDATE_URL);
if ($url === false) {
exit('URL Geçerli Değil !!!');
}
}
Integer Doğrulama
filter_var()
fonksiyonu ile FILTER_VALIDATE_INT
flagini kullanarak integer değerleri doğrulayabiliriz. Bu metodu kullanmamızın avantajı, string olarak gelen integer değerini integer sayıya dönüştürmesidir. Bizim tekrardan değeri integera dönüştürmemiz gerekmez.
if ( ! empty($_POST['number']))
{
$number = $_POST['number'];
$number = filter_var($number, FILTER_VALIDATE_INT);
if ($number === false) {
exit('Integer Geçerli Değil !!!');
}
}
Bu fonksiyone input gönderirken
- 25 (integer) değişmez
- "25" stringi integera dönüştürülür
- 25.11 (float)
FALSE
döner. Çünkü integer değil - TRUE (boolean)
1
döner(integer) - FALSE (boolean)
FALSE
döner (boolean) - arrayler, nesneler, stringler(numerik karakter içermeyen)
FALSE
döner
Not: bütün FALSE
değerleri integer olmadığı anlamına gelir.
Boolean Doğrulama
filter_var()
fonksiyonu ve FILTER_VALIDATE_BOOLEAN
flagi ile boolean değerler doğrulanabilir. Bu fonksiyon "on" "yes" "true" string değerlerinde (büyük küçük harf duyarlı değil) TRUE
değerini döndürür. Diğer her şeyde FALSE
döner.
Çoğu tarayıcı checkbox işaretlendiğinde "on" stringini gönderir. Bir örnekle görelim:
if (!empty($_POST['check']))
{
$check = $_POST['check'];
$check = filter_var($check, FILTER_VALIDATE_BOOLEAN);
}
fileter_var()
fonksiyonuna input değerini FILTER_VALIDATE_BOOLEAN
flagi ile gönderdiğimizde "on" string değeri TRUE
değerine dönüştürülecek. Bu sayede inputa boolean olarak davranabiliriz.
Biz bazı fonksiyonları şimdiye kadar gördük. Fakat hepsini burada anlaymaya çalışmak iyi bir pratik değil. Bunun için PHP nin dökümantasyonunu inceleyebilirsiniz. php data filtering Şimdi input validation için bir sınıf oluşturalım.
Input Doğrulama İçin Kendi Sınıfını Oluştur
Nesne yönelimli programlama ile ilgili bilginizin olduğunu düşünüyorum. Eğer yoksa internetten biraz araştırma yapabilirsiniz.
Sınıfımız XSS ve input doğrulama ile ilgilenecek. Sınıfın ismini Input
verdim.
class Input {
}
Fonksiyonları statik olarak tanımladım. Bu sayede fonksiyonları daha kolay çağırabiliriz. Sınıfımız şu fonksiyonları barındıracak:
check()
inputun boş oluş olmadığını kontrol edecekint()
integer değerleri doğrulayacakstr()
kaçış karakterleriyle ve boşluklarla ilgilenecekbool()
inputu boolean değere dönüştürecekemail()
email değeri doğrulayacakurl()
URL leri doğrulayacak
Tüm sınıfın kodu yaklaşık olarak şu şekilde:
class Input {
static $errors = true;
static function check($arr, $on = false)
{
if ($on === false)
{
$on = $_REQUEST;
}
foreach ($arr as $value)
{
if (empty($on[$value]))
{
self::throwError('Veri Yok', 900);
}
}
}
static function int($val)
{
$val = filter_var($val, FILTER_VALIDATE_INT);
if ($val === false)
{
self::throwError('Geçerli Bir Integer Değil', 901);
}
return $val;
}
static function str($val)
{
if (!is_string($val))
{
self::throwError('Geçerli Bir String Değil', 902);
}
$val = trim(htmlspecialchars($val));
return $val;
}
static function bool($val)
{
$val = filter_var($val, FILTER_VALIDATE_BOOLEAN);
return $val;
}
static function email($val)
{
$val = filter_var($val, FILTER_VALIDATE_EMAIL);
if ($val === false)
{
self::throwError('Geçerli Bir Email Değil', 903);
}
return $val;
}
static function url($val)
{
$val = filter_var($val, FILTER_VALIDATE_URL);
if ($val === false)
{
self::throwError('Geçerli Bir URL Değil', 904);
}
return $val;
}
static function throwError($error = "Bir Hata Oluştu", $errorCode = 0)
{
if (self::$errors === true)
{
throw new Exception($error, $errorCode);
}
}
}
Bu sınıfı bir dosyaya kaydedip kendi scriptiniz içinde include ederek kullanabilirsiniz. Bu sınıfın kullanımıyla ilgili bir kaç örnek görelim.
Hatalarla İlgilenme
Yukarıdaki kodda throwError
isminde hata üreten bir fonksiyon oluşturdum. Eğer $error
değişkeni TRUE
ise hata üretecek. Eğer hata oluşturmak istemiyorsanız değeri FALSE
olarak ayarlayın.
Input::$error = false;
Inputları Kontrol Etme
Inputları kontrol etmek için check
fonksiyonunu oluşturdum. Bu fonksiyonda 2 argüman var. 1. argüman kontrol edilecek elemanlar listeri(array). 2. argüman ise içinde arama yapılacak "süper global array". POST metodu için $_POST
, GET metodu için $_GET
, detault $_REQUEST
.
Input::check(['email', 'password'], $_POST);
Bu kodla "email" ve "password" $_POST
arrayi içinde bulunduğunu ve boş olmadığını kontrol edecek. Aksi durumda hata üretecek.
Doğrulama
Diğer fonksiyonları inputları doğrulamak için kullanabilirsiniz.
// integer doğrula
$number = Input::int($_POST['number']);
// string doğrula
$name = Input::str($_POST['name']);
// boolean'a dönüştür
$bool = Input::bool($_POST['boolean']);
// email doğrula
$email = Input::email($_POST['email']);
// URL doğrula
$url = Input::url($_POST['url']);
isset
ve empty
üzerine düşüncelerim
empty
fonksiyonu bir değişkenin boş olup olmadığını kontrol eder. Eğer değişken tanımlanmadıysa true döner.
php > var_dump( empty($hello) );
bool(true)
isset
ise bir değişken tanımlandıysa ve NULL değilse TRUE
döner.
php > var_dump( isset($hello) ); # $hello tanımlı değil
bool(false)
php > $hello = NULL;
php > var_dump( isset($hello) ); # hello tanımlı değeri NULL
bool(false)
php > $hello = "";
php > var_dump( isset($hello) ); # tanımlı değeri boş string
bool(true)
empty
fonksiyonunun şu özelliğinden dolayı kullanmayı pek tercih etmiyorum
php > var_dump( empty("0") );
bool(true)
"0"
değerinde TRUE
dönüyor. Ama string boş değil. Bu stringi sayı olarakmı değerlendiriyor bilmiyorum fakat inputtan string olarak 0 gelebileceği için isset
fonksiyonunu kullanıyorum. Bu sayede değişkenin tanımlandığını anlıyorum ve ardından diğer doğrulama (validation) işlemlerini yapıyorum. Bu tamamiyle kişisel bir tercih. Bu konu ile ilgili sizin düşünceniz nedir? twitter yada mail olarak bana bildirebilirsiniz.
Input Validation ve Output Encoding
Genellikle input doğrulaması sırasında htmlspecialchars()
fonksiyonunu kullandık. Bu fonksiyon html karakterlerini escape etti ve ondan sonra veritabanına kaydettik. Bu kullanılabilecek bir yöntemdir. Fakat daha yaygın ve önerilen yöntem şudur:
Kullanıcıdan bir veri aldığın zaman onu veritabanına olduğu gibi kaydet ve onu sunarken output encoding uygula. Çünkü veritabanına escape olarak kaydettikten sonra tekrar eski haline döndermesi zorudur. Ayrıca veritabanındaki değer herzaman html olarak sunulmayacak. Bazı durumlarda bir mobil uygulamya sunulacak. Bu gibi durumlarda htmlspecialchars
kullanmak pek iyi bir yöntem değildir. Bunun yerine ham veriyi veritabanına kaydedip sunulan platforma göre encoding uygulamak daha iyi bir pratiktir.
Son Söz
Bu yazı ile input validation vs XSS önleme ile ilgili giriş niteliğinde bilgi vermeye çalıştım. Input sınıfını istediğiniz gibi geliştirebilir ve kullanabilisiniz. Eğer bir sorunuz yada takıldığınız bir yer varsa bana sormaktan çekinmeyin. Umarım Faydalı olmuştur.
Saygılarımla :)