
PHP'de Oturum Yönetimi ve Kimlik Doğrulama
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.
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.
// 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.
$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.
$_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.
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.
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.
// ç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.
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,httponlyvesamesitetanımlı. session.use_strict_modeaçık.- Girişten hemen sonra
session_regenerate_idçağrılıyor. - Parolalar
password_hashile 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_equalsile 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
- Kurulum ve temel sözdizimi
- Döngüler ve akış kontrolü
- Diziler
- Fonksiyonlar
- String fonksiyonları
- Süper globaller
- Form işleme
- Dosya işlemleri
Dilin ayrıntıları
Nesne yönelimli PHP
- Sınıf ve nesne
- Kalıtım, arayüz ve trait
- Statik üyeler, sabitler ve enum
- Namespace, Composer ve autoload
Veri ve güvenlik
- PDO ile veritabanı işlemleri
- Oturum yönetimi ve kimlik doğrulama (bu yazı)
- Güvenli çıktı ve XSS
- JSON ile veri değişimi
Dış dünya ve üretim
Yorumlar
İlk yorumu sen yap.
İlgili Yazılar

PHP Uygulamasını Üretime Almak: Yapılandırma ve Güvenlik
Geliştirme makinesinde çalışan kod ile üretimde güvenle çalışan kod arasındaki fark yapılandırmadır. Ayarlar, izinler, dağıtım akışı ve son kontroller.

PHP ile REST API Yazmak: Yönlendirme ve HTTP Durum Kodları
Bir API'nin kalitesi çoğunlukla ayrıntılarda belli olur: doğru durum kodu, tutarlı hata biçimi ve öngörülebilir adresler. Saf PHP ile uçtan uca kurulum.

PHPUnit ile Birim Test Yazmak: Kurulum, Sahte Nesneler ve Kapsam
Testin amacı hata bulmak değil, değişikliği güvenle yapabilmektir. Kurulum, ilk test, sahte nesneler ve test edilebilir kodun özellikleri.