PHP Güvenli Girdi Nasıl Alınır ve Doğrulanır?
PHP'de güvenli girdi almak için kullanıcıdan gelen her veri filter_var(), preg_match() ve htmlspecialchars() gibi doğrulama ve temizleme fonksiyonlarından geçirilmeli, veritabanı işlemlerinde de hazırlanmış sorgular (prepared statements) kullanılmalıdır. Kısacası hiçbir kullanıcı girdisine olduğu gibi güvenilmemeli; her veri önce doğrulanmalı, sonra uygun formata dönüştürülüp işlenmelidir. Bu yaklaşım, web uygulamalarında en sık görülen XSS ve SQL injection gibi saldırıları büyük ölçüde engeller.
Aşağıda bu güvenli girdi alma sürecinin temel adımlarını ve PHP'nin sunduğu araçları örneklerle inceleyeceğiz.
Güvenli Girdi Almak Neden Önemli?

Web uygulamalarının büyük çoğunluğu kullanıcıdan bir şekilde veri alır: form gönderimleri, URL parametreleri, arama kutuları, giriş ekranları... Bu veri akışı, uygulamanın işlevselliği için gerekliyken aynı zamanda en büyük güvenlik açığı kaynağıdır. Çünkü kullanıcı tarafından gönderilen her veri, niyeti kötü olan biri tarafından da manipüle edilebilir.
İki klasik saldırı türü bu riski açıkça gösterir. XSS (Cross-Site Scripting) saldırısında, kötü niyetli bir kullanıcı form alanına JavaScript kodu enjekte eder; bu kod doğrulanmadan sayfaya yazdırılırsa, siteyi ziyaret eden diğer kullanıcıların tarayıcısında çalışarak oturum çalma veya yönlendirme gibi zararlar verebilir. SQL injection saldırısında ise girdi, veritabanı sorgusuna doğrudan eklenirse saldırgan sorgunun yapısını değiştirip yetkisiz veri okuma, silme veya değiştirme işlemleri yapabilir.
Bu tehditlerin ortak noktası şudur: geliştirici, kullanıcı girdisinin "temiz" ve "beklenen formatta" olacağını varsaymıştır. Güvenli PHP geliştirmenin temel prensibi tam olarak bunun tersidir: hiçbir kullanıcı girdisine güvenme. Bir form alanı sayı bekliyor diye kullanıcının gerçekten sayı göndereceğinin garantisi yoktur; istemci taraflı doğrulamalar (JavaScript, HTML required niteliği gibi) tarayıcı geliştirici konsolundan veya doğrudan HTTP isteğiyle kolayca atlatılabilir. Bu nedenle asıl doğrulama her zaman sunucu tarafında, PHP kodu içinde yapılmalıdır.
İlerleyen bölümlerde PHP'nin girdi kaynaklarını, doğrulama fonksiyonlarını ve çıktı temizleme mekanizmalarını sırayla ele alacağız.
PHP'de Girdi Kaynakları: $_GET, $_POST, $_REQUEST
PHP'de kullanıcı girdisi genellikle üç superglobal dizi üzerinden erişilir ve her birinin kullanım amacı farklıdır.
$_GET, URL'nin sorgu string'i üzerinden gönderilen parametreleri tutar (ornek.php?id=5 gibi). Bu veriler tarayıcı adres çubuğunda görünür olduğundan hassas bilgiler (şifre, kişisel veri) için kullanılmamalıdır. Genellikle sayfalama, filtreleme veya kayıt kimliği gibi işlemlerde tercih edilir.
$_POST, HTML formlarının method="post" ile gönderilen verilerini taşır. Veri istek gövdesinde (body) iletildiği için URL'de görünmez, bu yüzden giriş formları, kayıt işlemleri gibi daha hassas veri gönderimlerinde kullanılır. Ancak URL'de görünmemesi, verinin şifrelendiği veya güvenli olduğu anlamına gelmez; HTTPS kullanılmadığı sürece bu veri de ağ üzerinde açık şekilde iletilebilir.
$_REQUEST ise $_GET, $_POST ve $_COOKIE verilerini birleştiren bir superglobal'dir. Kullanışlı görünse de üretim kodunda önerilmez, çünkü verinin hangi kaynaktan geldiği belirsizleşir. Bu durum, aynı isimde bir GET parametresi ile POST verisinin çakışması durumunda beklenmeyen davranışlara ve saldırganın hangi kanalı kullanacağını tahmin etmesini kolaylaştıran bir belirsizliğe yol açabilir. Bu nedenle veri kaynağı her zaman açık ve öngörülebilir olmalıdır: form verisi için $_POST, URL parametresi için $_GET kullanılmalıdır.
filter_var() ile Veri Doğrulama
PHP'nin filter_var() fonksiyonu, girdiyi belirli bir filtreye göre doğrulamak veya temizlemek için kullanılan yerleşik bir araçtır. Fonksiyon, veri belirtilen filtreye uymuyorsa false döndürür; bu da doğrulamayı basit bir koşul kontrolüne indirger.
E-posta doğrulaması en yaygın kullanım örneklerinden biridir:
$email = $_POST['email'] ?? '';
if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
echo "Geçerli e-posta adresi.";
} else {
echo "Geçersiz e-posta adresi.";
}
Sayısal girdilerde de FILTER_VALIDATE_INT ve FILTER_VALIDATE_FLOAT filtreleri kullanılır. Örneğin bir yaş alanının tam sayı olup olmadığını kontrol etmek için:
$age = $_POST['age'] ?? '';
$validAge = filter_var($age, FILTER_VALIDATE_INT, [
'options' => ['min_range' => 0, 'max_range' => 120]
]);
if ($validAge === false) {
echo "Geçersiz yaş değeri.";
} else {
echo "Yaş: " . $validAge;
}
min_range ve max_range seçenekleri, sadece sayı formatını değil, mantıklı bir aralıkta olup olmadığını da kontrol etmeye imkân verir. Benzer şekilde FILTER_VALIDATE_URL bir bağlantının geçerli formatta olup olmadığını, FILTER_SANITIZE_STRING gibi temizleme filtreleri (not: bu filtre PHP 8.1 ile kullanımdan kaldırılmıştır) ise girdiden istenmeyen karakterleri ayıklamak için geçmişte kullanılmıştır; güncel projelerde temizleme işlemleri için htmlspecialchars() gibi bağlam bazlı fonksiyonlar tercih edilmelidir.
filter_var()'ı özellikle format doğrulaması gerektiren alanlarda (e-posta, URL, IP adresi, sayı) kullanmak, elle regex yazmaktan daha az hataya açık ve daha okunabilir bir çözümdür.
preg_match() ile Desen Bazlı Doğrulama
filter_var() genel doğrulama filtreleri sunar ama bazen daha spesifik kurallar gerekir. Örneğin bir kullanıcı adının sadece harf, rakam ve alt çizgi içermesini, belirli bir uzunluk aralığında olmasını istiyorsanız preg_match() ile düzenli ifade (regex) kullanmak daha uygun olur.
Kullanıcı adı doğrulama örneği:
$username = $_POST['username'] ?? '';
if (preg_match('/^[a-zA-Z0-9_]{3,20}$/', $username)) {
echo "Kullanıcı adı geçerli.";
} else {
echo "Kullanıcı adı 3-20 karakter olmalı ve sadece harf, rakam, alt çizgi içermeli.";
}
Bu regex, baştan sona (^ ve $ ile) sadece izin verilen karakterleri kabul eder. Aradaki kısmi eşleşmeleri engellediği için kullanıcı adının içine istenmeyen karakterlerin sızması önlenir.
Yaş doğrulama gibi sayısal ama biçim kısıtlı alanlarda da regex işe yarar:
$age = $_POST['age'] ?? '';
if (preg_match('/^[1-9][0-9]{0,2}$/', $age)) {
$age = (int) $age;
echo "Geçerli yaş: " . $age;
} else {
echo "Geçersiz yaş değeri.";
}
Burada 0 ile başlayan veya üç haneden uzun değerler reddedilir. Regex yazarken dikkat edilmesi gereken nokta, kuralın gerçekten ihtiyacı karşıladığından emin olmaktır; aşırı gevşek bir desen istenmeyen girdileri süzemez, aşırı katı bir desen ise geçerli kullanıcıları reddedebilir. Karmaşık regex'lerde preg_match() sonucunu her zaman kontrol edin, çünkü hatalı bir desen false döndürebilir ve bu durumu doğrulama başarısızlığından ayırt etmek gerekir.
htmlspecialchars() ile Çıktı Temizleme

Girdiyi doğrulamak tek başına yeterli değildir; veriyi HTML sayfasına yazdırırken de önlem almak gerekir. Kullanıcının gönderdiği bir metin doğrudan sayfaya basılırsa, içine <script> gibi etiketler eklenerek XSS (Cross-Site Scripting) saldırısı gerçekleştirilebilir. htmlspecialchars() fonksiyonu, HTML açısından özel anlam taşıyan karakterleri (<, >, &, ", ') karşılık gelen HTML entity'lerine çevirerek bu riski ortadan kaldırır.
$comment = $_POST['comment'] ?? '';
echo htmlspecialchars($comment, ENT_QUOTES, 'UTF-8');
ENT_QUOTES bayrağı hem çift hem tek tırnak karakterlerinin kaçışlanmasını sağlar. PHP 8.1 itibarıyla htmlspecialchars() için varsayılan bayrak ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 olarak güncellendi, yani flag parametresini atlasanız da tek tırnaklar artık varsayılan olarak kaçışlanıyor. Yine de kodun hangi PHP sürümünde çalışacağı belirsizse, bayrağı ve karakter setini açıkça yazmak hem okunabilirlik hem tutarlılık açısından daha güvenli bir tercihtir.
Bu fonksiyonu, veriyi veritabanına kaydederken değil, ekrana yazdırırken kullanmak önemlidir. Veriyi olduğu haliyle saklayıp sadece çıktı anında kaçışlamak, verinin farklı bağlamlarda (API, log, e-posta) yeniden kullanılmasını kolaylaştırır.
Hazırlanmış Sorgular (Prepared Statements)
Kullanıcı girdisi bir SQL sorgusuna doğrudan eklenirse SQL injection saldırısına açık kapı bırakılır. Bunun önündeki en etkili çözüm, sorgu ile veriyi birbirinden ayıran hazırlanmış sorgulardır (prepared statements). PDO ile bu şu şekilde uygulanır:
$pdo = new PDO(
'mysql:host=localhost;dbname=test;charset=utf8mb4',
'kullanici_adi',
'sifre',
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false,
]
);
$stmt = $pdo->prepare('SELECT id, name FROM users WHERE email = :email');
$stmt->execute(['email' => $email]);
$user = $stmt->fetch();
PDO::ATTR_EMULATE_PREPARES => false ayarı, sorguların PHP tarafında taklit edilmeden doğrudan veritabanı sürücüsü tarafından hazırlanmasını sağlar; bu da gerçek anlamda parametre ayrımı yaparak injection riskini azaltır. ERRMODE_EXCEPTION ise hataların sessizce yutulmasını önleyip try-catch ile yönetilebilir hale getirir. Kullanıcıdan gelen hiçbir değer sorgu metnine string birleştirme ile eklenmemeli, her zaman :email gibi adlandırılmış veya ? gibi konumsal parametreler üzerinden bağlanmalıdır.
Sık Sorulan Sorular
filter_var() mi, preg_match() mi kullanmalı? İkisi farklı işler yapar. filter_var(), e-posta, URL, sayı gibi standart formatları doğrularken hazır ve test edilmiş bir yöntem sunar. preg_match() ise kullanıcı adı, posta kodu gibi kendine özgü kurallar gerektiren durumlarda devreye girer. Standart bir format varsa filter_var(), özel bir desen gerekiyorsa preg_match() tercih edilmelidir.
$_REQUEST kullanılmalı mı? Genelde önerilmez. $_REQUEST, GET, POST ve cookie verilerini aynı çatı altında topladığı için verinin nereden geldiğini belirsizleştirir. Bu durum hem hata ayıklamayı zorlaştırır hem de güvenlik açısından öngörülemeyen sonuçlara yol açabilir. Yerine, veriyi hangi yöntemle bekliyorsanız $_GET veya $_POST kullanmak daha nettir.
XSS nasıl önlenir? XSS saldırıları genellikle kullanıcıdan alınan verinin doğrudan HTML içine yazılmasından kaynaklanır. Bunu önlemek için ekrana basılacak her veri htmlspecialchars() ile işlenmeli, özel karakterler HTML karşılıklarına dönüştürülmelidir. Bu basit adım, script enjeksiyonu gibi birçok saldırıyı etkisiz hale getirir.
Veri her zaman mı doğrulanmalı? Evet. Kullanıcıdan, dış API'den veya formdan gelen her veri güvenilmez kabul edilmelidir. Doğrulama adımı atlanırsa, görünürde zararsız bir alan bile veritabanı sorgularını veya sayfa çıktısını bozabilir. Girdi doğrulama ve çıktı temizleme birlikte uygulandığında uygulama güvenliği ciddi ölçüde artar.
Sonuç olarak, PHP'de güvenli girdi almak tek bir fonksiyona değil, doğrulama, temizleme ve hazırlanmış sorgu kullanımının bir araya geldiği bütünsel bir yaklaşıma dayanır. Bu adımları alışkanlık haline getirmek, uygulamaların uzun vadede daha sağlam ve güvenilir olmasını sağlar.