CSRF nedir, nasıl önlenir?
CSRF (Cross-Site Request Forgery, siteler arası istek sahteciliği), oturumu açık bir kullanıcının tarayıcısına, kullanıcının haberi olmadan sitenize istek gönderttirilmesidir. Tarayıcı, sitenize giden her isteğe o siteye ait çerezleri kendiliğinden ekler. Kullanıcı sitenizde oturum açmışken başka bir sayfayı ziyaret ederse, o sayfa tarayıcıya sitenize doğru bir istek yaptırabilir ve istek kullanıcının oturumuyla işlenir.
Sonuç, kullanıcının yapabildiği her işlemin onun adına yapılabilmesidir: e-posta adresini değiştirmek, kayıt silmek, yönetici panelinde yeni kullanıcı eklemek. Saldırgan yanıtı göremez; hedef veri okumak değil işlem yaptırmaktır. Çözüm, isteğin gerçekten sizin sayfanızdan geldiğini doğrulamaktır.
CSRF nasıl işler, nerede durdurulur?
-
Tarayıcı çerezi kendiliğinden gönderir
Sorunun kaynağı, sunucunun isteği yalnızca oturum çerezine bakarak kabul etmesidir. Çerez her istekte kendiliğinden gittiği için sunucu, isteği kullanıcının kendi isteğiyle mi yoksa başka bir sitenin tetiklemesiyle mi yaptığını ayırt edemez.
-
Belirteç, isteğin sizin sayfanızdan geldiğini kanıtlar
CSRF belirteci (token), sunucunun ürettiği, oturuma bağlı, tahmin edilemeyen rastgele bir değerdir. Formlarınıza gizli alan olarak eklenir ve istek geldiğinde sunucuda doğrulanır. Başka bir site bu değeri bilemeyeceği için tetiklediği istek reddedilir.
-
Katmanlar birbirini tamamlar
Belirteç asıl korumadır.
SameSiteçerez özelliği tarayıcı düzeyinde ek koruma sağlar;Originbaşlığının denetimi üçüncü bir kontrol ekler; durum değiştiren işlemlerin GET ile yapılmaması ise bu korumaların çalışmasının ön koşuludur.
Belirtiler: nasıl fark edilir?
- Kod incelemesinde: Veri değiştiren formlarda gizli belirteç alanı yoksa ya da sunucu bu alanı doğrulamıyorsa koruma yoktur. Silme, onaylama, durum değiştirme gibi işlemler bir bağlantıyla (GET) yapılıyorsa açık kesindir.
- Tarayıcıda: Geliştirici araçlarında oturum çerezinin
SameSiteözelliğine bakın; tanımsız ya daNoneise ek koruma yoktur. - Kullanıcı tarafında: Kullanıcının yapmadığını söylediği ayar değişiklikleri, onun adına eklenmiş ya da silinmiş kayıtlar.
- Günlüklerde: Durum değiştiren isteklerde
Refererya daOrigindeğerinin sitenizle ilgisiz bir adres olması.
CSRF belirteci uygulaması
Belirteç oturum başına bir kez üretilir, oturumda saklanır ve her formda gizli alan olarak gönderilir. Karşılaştırma hash_equals ile yapılır; bu işlev zamanlama farkından bilgi sızmasını önler.
<?php
session_start();
function csrfBelirteci(): string
{
if (empty($_SESSION['csrf'])) {
$_SESSION['csrf'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf'];
}
function csrfDogrula(?string $gelen): bool
{
return is_string($gelen)
&& !empty($_SESSION['csrf'])
&& hash_equals($_SESSION['csrf'], $gelen);
}
Formda kullanımı:
<form method="post" action="profil-guncelle">
<input type="hidden" name="csrf" value="<?= htmlspecialchars(csrfBelirteci(), ENT_QUOTES, 'UTF-8') ?>">
<label for="eposta">E-posta</label>
<input type="email" id="eposta" name="eposta" required>
<button type="submit">Kaydet</button>
</form>
İsteği işlerken, başka hiçbir şey yapmadan önce doğrulayın:
<?php
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
header('Allow: POST');
echo 'Bu işlem yalnızca form gönderimiyle yapılabilir.';
return;
}
if (!csrfDogrula($_POST['csrf'] ?? null)) {
http_response_code(403);
echo 'Oturumunuz doğrulanamadı. Sayfayı yenileyip tekrar deneyin.';
return;
}
// ... doğrulama geçti: işlemi yap
Dikkat edilecekler:
- Belirteci URL'ye koymayın; adres çubuğu, günlükler ve
Refererbaşlığı üzerinden sızabilir. - Giriş yapıldığında oturum kimliğiyle birlikte belirteci de yenileyin.
- Giriş formunun kendisini de koruyun; aksi hâlde kullanıcı, farkında olmadan başkasının hesabına giriş yaptırılabilir.
- Laravel, Symfony gibi çerçeveler ve çoğu içerik yönetim sistemi hazır CSRF koruması sunar; kendi çözümünüzü yazmak yerine onu kullanın ve devre dışı bırakmayın.
AJAX ve API istekleri
JavaScript ile gönderilen isteklerde belirteç genellikle bir meta etiketinden okunur ve özel bir istek başlığıyla gönderilir. Sunucu, başlıktaki değeri aynı şekilde doğrular.
// Sayfada: <meta name="csrf" content="...sunucunun ürettiği belirteç...">
var belirtec = document.querySelector('meta[name="csrf"]').content;
fetch('sepet/ekle', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': belirtec },
credentials: 'same-origin',
body: JSON.stringify({ urun: 12, adet: 1 })
});
// Sunucuda: csrfDogrula($_SERVER['HTTP_X_CSRF_TOKEN'] ?? null)
Kimliği çerezle değil, her istekte Authorization başlığıyla gönderilen bir belirteçle doğrulayan API'lerde tarayıcı kimlik bilgisini kendiliğinden eklemediği için CSRF riski farklıdır. Çerez kullanan her uç nokta ise korunmalıdır.
SameSite çerez özelliği
SameSite, çerezin başka sitelerden başlatılan isteklerle gönderilip gönderilmeyeceğini belirler:
- Strict: Çerez yalnızca aynı siteden başlatılan isteklerle gönderilir. En güvenlisidir; ancak kullanıcı başka bir siteden (ör. e-postadaki bağlantıdan) geldiğinde ilk istekte oturumu görünmez.
- Lax: Çerez, başka siteden gelen üst düzey gezinmelerde (bağlantıya tıklama) gönderilir; başka siteden yapılan POST isteklerinde gönderilmez. Çoğu site için uygun dengedir.
- None: Çerez her istekte gönderilir;
Secureile birlikte kullanılmak zorundadır. Yalnızca gerçekten üçüncü taraf bağlamında çalışması gereken çerezler içindir.
<?php
// Oturum çerezi (session_start'tan önce)
session_set_cookie_params([
'lifetime' => 0,
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
]);
session_start();
// Kendi çerezleriniz
setcookie('tercih', 'koyu', [
'expires' => time() + 60 * 60 * 24 * 30,
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
]);
Birçok güncel tarayıcı, özelliği belirtilmemiş çerezleri Lax gibi ele alır; ancak buna güvenmeyin, değeri açıkça belirtin. SameSite tek başına yeterli sayılmaz: Lax, GET ile yapılan durum değişikliklerini korumaz ve alt alan adlarınızdan biri ele geçirilirse "aynı site" sayılır. Belirteçle birlikte kullanın.
Origin ve Referer denetimi
Tarayıcılar, başka bir kaynaktan başlatılan POST isteklerine Origin başlığını ekler. Durum değiştiren isteklerde bu başlığın kendi alan adınızla eşleştiğini denetlemek, belirtece ek bir kontroldür. Başlık yoksa Referer değerine bakılabilir; ikisi de yoksa isteği belirteç doğrulamasına bırakın.
<?php
function ayniKaynak(): bool
{
$kaynak = $_SERVER['HTTP_ORIGIN'] ?? ($_SERVER['HTTP_REFERER'] ?? '');
if ($kaynak === '') {
return true; // başlık yok: karar belirteç doğrulamasına kalır
}
$gelen = strtolower((string) parse_url($kaynak, PHP_URL_HOST));
$izinli = ['www.ornek-site.com', 'ornek-site.com']; // kendi alan adlarınız
return in_array($gelen, $izinli, true);
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && !ayniKaynak()) {
http_response_code(403);
echo 'İstek kabul edilmedi.';
return;
} Durum değiştiren işlemlerde GET kullanmayın
GET istekleri yalnızca veri okumalıdır. Silme, onaylama, sipariş verme, ayar değiştirme gibi işlemler ?islem=sil&id=5 biçiminde bir bağlantıyla yapılıyorsa, o adresi içeren herhangi bir sayfa işlemi tetikleyebilir; ayrıca arama motoru botları ve tarayıcının ön yükleme özelliği de bu bağlantıları izleyebilir.
- Bu tür işlemleri küçük bir POST formuna çevirin; bağlantı görünümü isteniyorsa düğme CSS ile biçimlendirilebilir.
- Sunucuda istek yöntemini denetleyin; POST beklenen yerde GET'i reddedin.
- Oturumu kapatma işlemi de POST ile yapılmalıdır.
<!-- YANLIŞ: <a href="kayit-sil?id=5">Sil</a> -->
<form method="post" action="kayit-sil">
<input type="hidden" name="csrf" value="<?= htmlspecialchars(csrfBelirteci(), ENT_QUOTES, 'UTF-8') ?>">
<input type="hidden" name="id" value="<?= (int) $kayit['id'] ?>">
<button type="submit">Sil</button>
</form> Kritik işlemlerde yeniden doğrulama
Parola ve e-posta değişikliği, ödeme bilgisi güncelleme, yönetici ekleme gibi kritik işlemlerde kullanıcıdan mevcut parolasını ya da iki adımlı doğrulama kodunu yeniden isteyin. Bu önlem hem CSRF'ye hem de açık bırakılmış oturumların kötüye kullanılmasına karşı etkilidir.
Unutmayın: sitenizde bir XSS açığı varsa saldırganın betiği sayfadaki belirteci okuyabilir ve CSRF korumasını aşabilir. İki korumayı birlikte düşünün.
Sık yapılan hatalar
- Belirteci üretip doğrulamamak: Formda gizli alan vardır ama sunucu değeri karşılaştırmaz. Koruma, doğrulama satırındadır.
- Boş değeri kabul etmek: Oturumda belirteç yokken gelen boş değerle boş değerin "eşit" çıkması. Doğrulamada iki tarafın da dolu olduğunu denetleyin.
- Yalnızca bazı formları korumak: Yönetim panelindeki silme, sıralama, durum değiştirme gibi küçük işlemler unutulur. Koruma, tek tek formlara değil, tüm POST isteklerine ortak bir noktada uygulanmalıdır.
- Belirteci çerezde tutup yalnızca çereze bakmak: Çerez zaten her istekte kendiliğinden gider; karşılaştırılacak değer formdan ya da istek başlığından gelmelidir.
- GET isteğini POST gibi işlemek:
$_REQUESTkullanan kod, GET ile gelen parametreleri de kabul eder. İstek yöntemini açıkça denetleyin ve veriyi$_POSTüzerinden okuyun. - JSON uç noktalarını unutmak: "Bu adres yalnızca JavaScript'ten çağrılıyor" varsayımı koruma değildir; çerezle kimlik doğrulayan her uç nokta belirteç istemelidir.
- Hata durumunda işlemi sürdürmek: Doğrulama başarısızsa istek orada sonlanmalı; uyarı gösterip işlemi yine de yapmak korumayı anlamsız kılar.
Kontrol listesi
- Veri değiştiren her formda CSRF belirteci var ve sunucuda
hash_equalsile doğrulanıyor. - AJAX istekleri belirteci başlıkla gönderiyor.
- Giriş ve çıkış işlemleri de korunuyor.
- Durum değiştiren hiçbir işlem GET ile yapılmıyor.
- Oturum çerezi
SameSite=Laxya daStrict, ayrıcaSecureveHttpOnly. - Origin denetimi ek katman olarak uygulanıyor.
- Kritik işlemlerde parola ya da ikinci adım yeniden isteniyor.
- Çerçevenin hazır CSRF koruması açık; istisna tanımlanan adresler gözden geçirildi.
Sık sorulan sorular
SameSite=Lax kullanıyorum; belirtece yine de gerek var mı?
Evet. SameSite güçlü bir ek katmandır ama GET ile yapılan değişiklikleri korumaz, eski tarayıcılarda bulunmayabilir ve alt alan adlarınız "aynı site" sayılır. Belirteçle birlikte kullanın.
CSRF ile XSS arasındaki fark nedir?
XSS'te saldırganın betiği sizin sayfanızda çalışır. CSRF'te betik çalışmaz; kullanıcının tarayıcısı başka bir siteden sizin sitenize istek göndermeye yönlendirilir. XSS açığı CSRF korumasını da aşabilir.
Her form için ayrı belirteç mi üretmeliyim?
Oturum başına tek belirteç çoğu site için yeterlidir ve geri düğmesi, çoklu sekme gibi durumlarda sorun çıkarmaz. Her istekte yenilenen belirteç daha sıkıdır ama kullanılabilirliği zorlaştırır.
Captcha CSRF'yi önler mi?
Dolaylı olarak zorlaştırır ama amacı bu değildir ve her formda kullanılamaz. CSRF için doğru araç belirteçtir.
BYK Yazılım Destek Ekibi
Bu rehber BYK Yazılım destek ekibi tarafından hazırlanır ve düzenli olarak gözden geçirilir. Son güncelleme: 04.10.2026.
İlgili rehberler
Web sitenizi birlikte gözden geçirelim
BYK Yazılım kurumsal web siteleri geliştirir. Sitenizle ilgili sorularınız için bize yazabilirsiniz.
Bize ulaşın Kurumsal web sitesi hizmetimiz