PHP Form İşleme: Doğrulama, CSRF ve Güvenli Kayıt

PHP Form İşleme: Doğrulama, CSRF ve Güvenli Kayıt

4 dakika okuma

Form işleme, kullanıcıdan veri alan her uygulamanın temel akışı. Akış üç adımdan oluşur: veriyi okumak, doğrulamak ve kalıcı hale getirmek. Bu adımların herhangi birinde atlanan kontrol, doğrudan güvenlik açığına dönüşür.

Akışın tamamı tek bir varsayım üzerine kurulur: tarayıcıdan gelen hiçbir değer güvenilir değildir. Alan adı, alan sayısı, alan tipi ve uzunluk dahil her şey istek gönderen tarafça değiştirilebilir. Sunucu tarafı doğrulama bu yüzden isteğe bağlı bir iyileştirme değil, akışın zorunlu parçasıdır.

HTML tarafı ve yöntem seçimi

Veriyi değiştiren işlemler POST ile gönderilir. GET yalnızca okuma amaçlı isteklerde, örneğin arama ve filtrelemede kullanılır; çünkü GET istekleri tarayıcı tarafından önbelleğe alınabilir ve adres olarak paylaşılabilir:

PHP5 satır
<form method="post" action="/kayit.php">
    <input type="text" name="ad" required>
    <input type="email" name="eposta" required>
    <button type="submit">Gönder</button>
</form>

HTML tarafındaki required ve type bildirimleri kullanım kolaylığı sağlar, güvenlik sağlamaz. Tarayıcı kontrolleri kolayca devre dışı bırakılabildiği için sunucu tarafında aynı kontrollerin tekrarlanması zorunludur.

Veriyi okumak ve doğrulamak

Doğrulama, verinin beklenen biçimde olup olmadığını kontrol eder. Hatalar tek tek toplanır ve tamamı kullanıcıya birlikte gösterilir; ilk hatada durmak kötü bir deneyim üretir:

PHP12 satır
$hatalar = [];

$ad = trim($_POST['ad'] ?? '');
$eposta = trim($_POST['eposta'] ?? '');

if (mb_strlen($ad) < 2) {
    $hatalar['ad'] = 'Ad en az iki karakter olmalı.';
}

if (!filter_var($eposta, FILTER_VALIDATE_EMAIL)) {
    $hatalar['eposta'] = 'Geçerli bir eposta adresi girin.';
}

Doğrulama ile temizleme birbirinden ayrı işlerdir. Doğrulama veriyi reddeder ya da kabul eder; temizleme ise veriyi değiştirir. İkisi karıştırıldığında kullanıcının girdiği değer sessizce başkalaşır.

Hataları gösterirken kaçış

Form hatalı doldurulduğunda önceki değerlerin ekrana geri basılması gerekir. Bu noktada kaçış yapılmazsa siteler arası betik çalıştırma açığı oluşur. Ekrana basılan her kullanıcı verisi htmlspecialchars ile kaçırılmalıdır:

PHP6 satır
&lt;input type="text" name="ad"
       value="&lt;?= htmlspecialchars($ad, ENT_QUOTES, 'UTF-8') ?&gt;"&gt;

&lt;?php if (isset($hatalar['ad'])): ?&gt;
    &lt;p class="hata"&gt;&lt;?= htmlspecialchars($hatalar['ad'], ENT_QUOTES, 'UTF-8') ?&gt;&lt;/p&gt;
&lt;?php endif; ?&gt;

CSRF koruması

Siteler arası istek sahteciliği, kullanıcının oturumunu kullanarak farkında olmadan işlem yaptırmaya dayanır. Korunma yöntemi, forma oturumda saklanan rastgele bir belirteç eklemek ve gelen isteği bu belirtece göre doğrulamaktır:

PHP11 satır
// form üretilirken
if (empty($_SESSION['csrf'])) {
    $_SESSION['csrf'] = bin2hex(random_bytes(32));
}

// form içinde gizli alan olarak gönderilir
// işleme tarafında
if (!hash_equals($_SESSION['csrf'], $_POST['csrf'] ?? '')) {
    http_response_code(419);
    exit;
}

Karşılaştırmada hash_equals kullanılır. Normal eşitlik karşılaştırması, eşleşmeyen ilk karakterde sonlandığı için zamanlama üzerinden bilgi sızdırabilir.

Veritabanına yazmak

Doğrulanan veri hazır sorgu ile yazılır. Değerlerin sorgu metnine birleştirilmesi SQL injection açığı üretir; konunun ayrıntısı PDO ile güvenli sorgu yazımı yazısında ele alınıyor:

PHP4 satır
$stmt = $db->prepare(
    'INSERT INTO kayitlar (ad, eposta, olusturuldu) VALUES (?, ?, NOW())'
);
$stmt->execute([$ad, $eposta]);

Gönderim sonrası yönlendirme

Kayıt başarılı olduğunda doğrudan çıktı üretmek yerine yönlendirme yapılır. Aksi halde kullanıcı sayfayı yenilediğinde form tekrar gönderilir ve aynı kayıt ikinci kez oluşur:

PHP2 satır
header('Location: /tesekkurler.php', true, 303);
exit;

Otomatik gönderimlere karşı sınırlama

Açık bir form, kısa sürede çok sayıda isteği tetikleyen otomatik araçların hedefi olur. Gizli alan tuzağı basit ve etkili bir ilk savunmadır: forma kullanıcıya görünmeyen bir alan eklenir, doldurulmuş olarak gelirse istek reddedilir.

PHP12 satır
// tuzak alan doldurulmuşsa istek otomatiktir
if (($_POST['website'] ?? '') !== '') {
    http_response_code(400);
    exit;
}

// aynı oturumdan art arda gönderimi sınırla
$son = $_SESSION['son_gonderim'] ?? 0;

if (time() - $son < 10) {
    $hatalar['genel'] = 'Lütfen biraz bekleyin.';
}

Daha yoğun trafikte adres tabanlı sayaçlar ve dış doğrulama servisleri devreye alınır. Ancak bu önlemlerin hiçbiri sunucu tarafı doğrulamanın yerini tutmaz.

Doğrulama kurallarını tek yerde toplamak

Doğrulama kodu form işleme betiğine dağıldığında, aynı kural birden fazla yerde farklı biçimde yazılmaya başlar. Kuralların tek bir yapıda tanımlanması hem tekrarı önler hem de yeni alan eklemeyi kolaylaştırır:

PHP14 satır
$kurallar = [
    'ad' => fn(string $v): bool => mb_strlen($v) >= 2,
    'eposta' => fn(string $v): bool => (bool) filter_var($v, FILTER_VALIDATE_EMAIL),
    'telefon' => fn(string $v): bool => preg_match('/^[0-9]{10}$/', $v) === 1,
];

$hatalar = [];
foreach ($kurallar as $alan => $kural) {
    $deger = trim($_POST[$alan] ?? '');

    if (!$kural($deger)) {
        $hatalar[$alan] = 'Geçersiz değer';
    }
}

Dosya alanı içeren formlarda form etiketine enctype değeri eklenmelidir. Bu değer eksik olduğunda dosya alanı sunucuya boş gelir ve hatanın nedeni ilk bakışta görünmez.

Kontrol listesi

  • Veriyi değiştiren istekler POST ile gönderilir.
  • Tarayıcı doğrulaması sunucu doğrulamasının yerini almaz.
  • Ekrana basılan her kullanıcı verisi kaçırılır.
  • Her formda CSRF belirteci bulunur ve hash_equals ile karşılaştırılır.
  • Veritabanı yazma işlemleri hazır sorgu ile yapılır.
  • Başarılı gönderim sonrasında yönlendirme yapılır.

PHP eğitim serisi

Bu yazı, temel sözdiziminden üretim ortamına kadar ilerleyen 25 bölümlük serinin bir parçası. Bölümler sırayla okunacak biçimde hazırlandı, ancak her biri tek başına da kullanılabilir.

Temeller

  1. Kurulum ve temel sözdizimi
  2. Döngüler ve akış kontrolü
  3. Diziler
  4. Fonksiyonlar
  5. String fonksiyonları
  6. Süper globaller
  7. Form işleme (bu yazı)
  8. Dosya işlemleri

Dilin ayrıntıları

  1. Tip sistemi ve strict_types
  2. Hata ve istisna yönetimi
  3. Düzenli ifadeler
  4. Tarih ve saat işlemleri

Nesne yönelimli PHP

  1. Sınıf ve nesne
  2. Kalıtım, arayüz ve trait
  3. Statik üyeler, sabitler ve enum
  4. Namespace, Composer ve autoload

Veri ve güvenlik

  1. PDO ile veritabanı işlemleri
  2. Oturum yönetimi ve kimlik doğrulama
  3. Güvenli çıktı ve XSS
  4. JSON ile veri değişimi

Dış dünya ve üretim

  1. cURL ile dış API çağırma
  2. Kendi REST API'ni yazmak
  3. PHPUnit ile birim test
  4. Hata ayıklama ve performans ölçümü
  5. Üretime alma ve güvenlik

Yorumlar

Bu form reCAPTCHA ile korunur; Google Gizlilik Politikası ve Hizmet Şartları geçerlidir.

İlk yorumu sen yap.

İlgili Yazılar