PHP'de Oturum Yönetimi ve Kimlik Doğrulama

PHP'de Oturum Yönetimi ve Kimlik Doğrulama

5 dakika okuma

Oturum, birbirinden bağımsız isteklerin aynı kullanıcıya ait olduğunu belirlemenin yoludur. Sunucu tarafında bir kayıt tutulur, kullanıcıya yalnızca bu kaydın anahtarı çerezle gönderilir. Sistemin güvenliği büyük ölçüde bu anahtarın ele geçirilememesine ve tahmin edilememesine bağlıdır.

Bu bölümde oturum yapılandırması, parolaların saklanması, giriş ve çıkış akışı ile sık karşılaşılan saldırı biçimlerine karşı alınacak önlemler ele alınıyor.

Oturum yapılandırması

Varsayılan ayarlarla başlatılan bir oturum, üretim için yeterli değildir. Çerez bayrakları ve katı kip, oturumun başlatılmasından önce tanımlanmalıdır.

PHP13 satır
ini_set('session.use_strict_mode', '1');   // sunucunun üretmediği kimlik kabul edilmez
ini_set('session.use_only_cookies', '1');  // kimlik adres satırından taşınmaz

session_set_cookie_params([
    'lifetime' => 0,           // tarayıcı kapanınca silinir
    'path' => '/',
    'domain' => '',
    'secure' => true,          // yalnızca HTTPS üzerinden gönderilir
    'httponly' => true,        // JavaScript erişemez
    'samesite' => 'Lax',       // siteler arası isteklerde gönderilmez
]);

session_start();

Katı kip önemlidir: bu ayar açık olmadığında saldırgan, kurbana kendi belirlediği bir oturum kimliğini kabul ettirebilir. Çerezin JavaScript tarafından okunamaması, siteler arası betik açığının oturum çalmaya dönüşmesini engeller.

Parolaların saklanması

Parolalar hiçbir koşulda geri döndürülebilir biçimde saklanmaz. Kullanılacak algoritma, özellikle yavaş çalışacak biçimde tasarlanmış bir parola özetleyicisidir. PHP bunun için hazır fonksiyonlar sunar ve tuz üretimi kendiliğinden yapılır.

PHP13 satır
// kayıt sırasında
$ozet = password_hash($parola, PASSWORD_DEFAULT);

// giriş sırasında
if (!password_verify($parola, $kullanici['parola_ozeti'])) {
    throw new DomainException('Kimlik bilgileri hatalı');
}

// algoritma ya da maliyet değiştiyse özeti yenilemek
if (password_needs_rehash($kullanici['parola_ozeti'], PASSWORD_DEFAULT)) {
    $yeni = password_hash($parola, PASSWORD_DEFAULT);
    $depo->parolaGuncelle($kullanici['id'], $yeni);
}

Özet uzunluğu algoritmaya göre değişir; sütun en az 255 karakter olacak biçimde tanımlanmalıdır. Parola için üst uzunluk sınırı konulmamalı, alt sınır ise karmaşıklık kuralları yerine uzunluk üzerinden belirlenmelidir.

Giriş akışı

Giriş işleminde kullanıcı adının bulunamaması ile parolanın yanlış olması birbirinden ayrılmaz. İki durum için farklı mesaj verilmesi, geçerli hesapların listelenmesine imkan tanır.

PHP13 satır
$kullanici = $depo->epostaIleBul($eposta);

if ($kullanici === null || !password_verify($parola, $kullanici['parola_ozeti'])) {
    $this->gecikmeUygula($eposta);

    throw new DomainException('E-posta ya da parola hatalı');
}

session_regenerate_id(true);    // oturum sabitlemeye karşı kimlik yenilenir

$_SESSION['kullanici_id'] = $kullanici['id'];
$_SESSION['giris_zamani'] = time();
$_SESSION['parmak_izi'] = hash('sha256', $_SERVER['HTTP_USER_AGENT'] ?? '');

Kimliğin girişten hemen sonra yenilenmesi zorunludur. Yenilenmediğinde, giriş öncesinde oluşturulmuş bir oturum kimliği giriş sonrasında da geçerli kalır ve o kimliği bilen biri hesaba erişebilir.

Çıkışta yalnızca oturum verisini boşaltmak yetmez; kayıt sunucudan silinir ve çerez de geçersiz kılınır.

PHP9 satır
$_SESSION = [];

if (ini_get('session.use_cookies')) {
    $p = session_get_cookie_params();
    setcookie(session_name(), '', time() - 42000, $p['path'], $p['domain'],
              $p['secure'], $p['httponly']);
}

session_destroy();

Kaba kuvvet denemeleri

Parola denemelerinin sınırlanmaması, zayıf parolaların er ya da geç bulunmasına yol açar. Sınırlama hem hesap hem de kaynak adresi bazında yapılmalıdır; yalnızca hesap bazında yapılan sınırlama, çok sayıda hesaba tek parola denenen saldırıları durdurmaz.

PHP21 satır
final class DenemeSayaci
{
    public function __construct(private readonly PDO $pdo) {}

    public function kayit(string $anahtar): void
    {
        $this->pdo->prepare(
            'INSERT INTO giris_denemeleri (anahtar, an) VALUES (?, ?)'
        )->execute([$anahtar, time()]);
    }

    public function engelliMi(string $anahtar, int $sinir = 5, int $pencere = 900): bool
    {
        $sorgu = $this->pdo->prepare(
            'SELECT COUNT(*) FROM giris_denemeleri WHERE anahtar = ? AND an > ?'
        );
        $sorgu->execute([$anahtar, time() - $pencere]);

        return (int) $sorgu->fetchColumn() >= $sinir;
    }
}

Gizli değerlerin karşılaştırılmasında sabit zamanlı karşılaştırma kullanılır. Sıradan karşılaştırma, ilk farklı karakterde durduğu için harcanan süre üzerinden bilgi sızdırabilir.

PHP3 satır
if (!hash_equals($beklenen, $gelen)) {
    throw new DomainException('Geçersiz anahtar');
}

Beni hatırla

Uzun süreli oturum açık tutma özelliği, oturum çerezinin ömrünü uzatarak değil, ayrı bir hatırlama anahtarıyla kurulur. Anahtarın kendisi veritabanında düz olarak saklanmaz; özetlenerek tutulur ve her kullanımda yenilenir.

PHP17 satır
// çerez üretimi
$secici = bin2hex(random_bytes(8));
$dogrulayici = bin2hex(random_bytes(32));

$depo->hatirlamaEkle(
    kullaniciId: $kullanici['id'],
    secici: $secici,
    ozet: hash('sha256', $dogrulayici),
    bitis: time() + 60 * 60 * 24 * 30,
);

setcookie('hatirla', $secici . ':' . $dogrulayici, [
    'expires' => time() + 60 * 60 * 24 * 30,
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Lax',
]);

Doğrulamada kayıt seçiciyle bulunur, ardından özetler sabit zamanlı olarak karşılaştırılır. Eşleşme sağlandığında eski kayıt silinip yenisi üretilir; böylece çalınan bir anahtarın ikinci kez kullanılması durumunda tutarsızlık fark edilebilir.

Oturum verisinin sınırları

Oturumda yalnızca kullanıcı kimliği ve az sayıda küçük değer tutulmalıdır. Kullanıcının tüm bilgilerinin oturuma yazılması, yetki değişikliklerinin yansımasını geciktirir: kullanıcı yönetici yetkisi kaybettiğinde, oturumdaki eski değer yürürlükte kalır.

Yetki denetimi her istekte kaynağından okunur. Bu, ek bir sorgu maliyeti getirir; ancak yetki bilgisi önbelleğe alınacaksa, yetkinin değiştiği anda önbelleği temizleyen bir mekanizma da kurulmalıdır.

Oturum ile belirteç arasındaki fark

Çerez tabanlı oturum, tarayıcıdan gelen istekler için uygundur. Mobil uygulamalar ve sunucular arası çağrılarda ise çerez taşınmaz; bu durumda istekle birlikte gönderilen bir belirteç kullanılır. İki yöntemin güvenlik gereksinimleri farklıdır.

Çerezle taşınan oturum, tarayıcı isteği kendiliğinden gönderdiği için siteler arası istek sahtekarlığına açıktır ve ayrı bir belirteçle korunması gerekir. Başlıkla gönderilen belirteç ise kendiliğinden gönderilmediği için bu saldırıya kapalıdır, ancak saklandığı yerin güvenliği uygulamanın sorumluluğundadır.

Oturum verisi nerede saklanır

Varsayılan olarak oturum verisi sunucudaki geçici dizine dosya olarak yazılır. Paylaşımlı barındırmada bu dizin başka hesaplarla ortak olabileceği için uygulamaya özel bir dizin tanımlanması gerekir. Birden fazla sunucuya yayılmış kurulumlarda ise dosya tabanlı saklama çalışmaz; istek başka sunucuya düştüğünde oturum bulunamaz.

Ortak bir depoya geçmek için kendi işleyicisi yazılabilir ya da hazır bir eklenti kullanılabilir. Hangi yöntem seçilirse seçilsin, eski oturumların temizlenme süresi ile çerez ömrünün tutarlı olması gerekir; aksi halde çerezi geçerli görünen kullanıcının verisi sunucudan silinmiş olur.

PHP4 satır
ini_set('session.save_path', '/var/lib/uygulama/oturum');
ini_set('session.gc_maxlifetime', '7200');    // sunucudaki kayıt ömrü
ini_set('session.sid_length', '48');
ini_set('session.sid_bits_per_character', '6');

Kontrol listesi

  • Oturum çerezinde secure, httponly ve samesite tanımlı.
  • session.use_strict_mode açık.
  • Girişten hemen sonra session_regenerate_id çağrılıyor.
  • Parolalar password_hash ile saklanıyor, özet yenileme denetimi var.
  • Hatalı giriş mesajı kullanıcı adı ile parolayı ayırmıyor.
  • Deneme sayısı hem hesap hem kaynak adresi bazında sınırlanıyor.
  • Gizli değer karşılaştırmaları hash_equals ile yapılıyor.
  • Oturumda yetki bilgisi değil yalnızca kimlik tutuluyor.
  • Uzun süreli oturumlar ayrı bir hatırlama anahtarıyla yönetiliyor, oturum çerezinin ömrü uzatılmıyor.

İlgili yazılar

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
  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 (bu yazı)
  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