Uygulamanızda kullanıcı girişi yapıldı, rotalar korundu, her şey harika görünüyor değil mi? Ancak perde arkasında, kullanıcınızın oturumunu (session) kullanarak onun adına işlem yapabilen "hayalet bir saldırgan" olabilir. İşte bu saldırı modeline CSRF (Cross-Site Request Forgery) diyoruz.
Küçük Bir Not: Bu rehberde CSRF korumasını iki ana senaryo üzerinden inceleyeceğiz:
Geleneksel Web (SSR): Node.js ve EJS gibi şablon motorlarının kullanıldığı yapı.
Modern Web (SPA/API): React, Vue gibi frontend framework'leri ile çalışan API tabanlı yapı.
Önce temel mantığı kavramak adına geleneksel yöntemden başlayacak, ardından modern mimarilerdeki farklara değineceğiz.
CSRF Nedir? Oturumunuz Nasıl "Kullanılır"?
CSRF (Siteler Arası İstek Sahteciliği), bir saldırganın, oturumu açık olan bir kullanıcıyı (farkında olmadan) kendi adına istenmeyen bir işlem yapması için kandırmasıdır.
En yaygın hedef alınan noktalar şunlardır:
Finansal İşlemler: Sizin adınıza para transferi yapılması veya kayıtlı kredi kartıyla alışveriş tetiklenmesi.
Hesap Yönetimi: E-posta adresinizin değiştirilerek hesabınızın ele geçirilmesi veya şifre sıfırlama talebi oluşturulması.
Yetki Suistimali: Bir admin kullanıcısının oturumu üzerinden sistemdeki diğer kullanıcıların silinmesi veya kritik ayarların değiştirilmesi.
Sosyal Etkileşim: Sizin adınıza istenmeyen paylaşımlar yapılması, birini takip etmeniz veya bir gönderiyi beğenmeniz.
Kısacası; bir "Gönder", "Güncelle" veya "Sil" butonu olan ve arkasında bir POST/PUT/DELETE isteği barındıran her form, eğer korunmuyorsa potansiyel bir CSRF hedefidir.
Saldırı Nasıl Gerçekleşir?
Oturumun Açılması: Kullanıcı bankacılık veya e-ticaret sitenizde oturum açar. Tarayıcı, sunucudan bir session cookie (oturum çerezi) alır.
Kötü Amaçlı Site: Kullanıcı aynı tarayıcıda gezerken sahte bir siteye veya zararlı bir linke tıklar.
Gizli İstek: Bu sahte site, arka planda sizin gerçek sunucunuza bir
POSTisteği (örneğin: para gönder veya siparişi onayla) gönderir.Güven Boşluğu: Tarayıcı, sizin sitenize giden her istekte olduğu gibi, oturum çerezini otomatik olarak isteğe ekler. Sunucunuz isteği alır, çereze bakar ve "Evet, bu kullanıcı giriş yapmış, istek geçerli!" diyerek işlemi onaylar.
Özet: Sunucu, isteğin sizin gerçek sayfanızdaki bir butondan mı, yoksa saldırganın sahte sayfasındaki gizli bir koddun mu geldiğini ayırt edemez.
Çözüm: CSRF Token (Güvenlik Mührü)
Bu saldırıdan korunmanın en etkili yolu, her hassas isteğe (POST, PATCH, DELETE) tahmin edilemez ve tek kullanımlık bir mühür eklemektir.
Mantık Şudur: Sunucu, sizin her görünümünüz (EJS sayfası) için benzersiz bir CSRF Token üretir. Bu token formun içine gizli bir alan olarak yerleştirilir. Saldırgan sizin çerezinizi tarayıcı sayesinde otomatik gönderse bile, sunucunun o an ürettiği gizli token'ı asla bilemez.
Node.js ve Express.js ekosisteminde CSRF korumasını teknik bir iş akışıyla nasıl uygulayacağımızı adım adım inceleyelim.
1. csurf Paketinin Kurulumu
Express.js tabanlı uygulamalarda CSRF koruması sağlamak için endüstri standardı olan csurf paketini kullanıyoruz. İlk adım olarak terminal üzerinden ilgili paketi projenize dahil edin:
npm install --save csurf2. Sunucu Tarafı Yapılandırması ve Ara Yazılım (Middleware) Sıralaması
csurf paketi, çalışmak için bir oturum (session) veya çerez (cookie) mekanizmasına ihtiyaç duyar. Bu nedenle, CSRF ara yazılımını tanımlarken sıralama hayati önem taşır. CSRF koruması, oturum verilerine erişebilmesi için mutlaka express-session yapılandırmasından sonra kodlanmalıdır.
const csrf = require('csurf');
const session = require('express-session');
const csrfProtection = csrf();
// 1. Önce Session yapılandırılır
app.use(session({
secret: 'your_secret_key',
resave: false,
saveUninitialized: false
}));
// 2. Ardından CSRF koruması aktif edilir
app.use(csrfProtection);3. Verilerin Tüm Görünümlere (Views) Aktarılması: res.locals
Uygulamanızdaki her bir route (rota) için manuel olarak token göndermek yerine, Express'in res.locals nesnesini kullanarak bu veriyi otomatize edebiliriz. Bu yöntem, tanımlanan değişkenlerin tüm EJS şablonlarında doğrudan erişilebilir olmasını sağlar.
app.use((req, res, next) => {
// res.locals üzerinden tanımlanan veriler tüm şablon motorlarına iletilir
res.locals.isAuthenticated = req.session.isLoggedIn;
res.locals.csrfToken = req.csrfToken();
next();
});4. Frontend Entegrasyonu: Form İçinde Token Kullanımı
csurf ara yazılımı aktif edildiğinde, sunucu GET, HEAD ve OPTIONS dışındaki tüm HTTP isteklerinde (POST, PUT, DELETE vb.) geçerli bir token doğrulaması bekler. Token bulunmayan istekler sunucu tarafından reddedilecektir. Bu doğrulamayı sağlamak için formlarınızın içine _csrf isminde gizli bir girdi alanı eklemeniz gerekir.
<form action="/update-data" method="POST">
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
<input type="text" name="data">
<button type="submit">Gönder</button>
</form>Teknik Kontrol Listesi
Uygulamanızın güvenliğini doğrulamak için şu teknik adımları kontrol edin:
Paket Bağımlılığı:
csurfpaketi projeye dahil edildi mi?Middleware Hiyerarşisi: CSRF ara yazılımı, oturum (session) yönetiminden sonra mı tanımlandı?
Global Değişken:
req.csrfToken()metodures.localsüzerinden tüm görünümlere servis edildi mi?Form Doğrulaması: Veri değişikliği yapan tüm
POSTformlarındaname="_csrf"özniteliğine sahip input alanı mevcut mu?
Bu yapılandırma tamamlandığında, sunucunuz her istekte tarayıcıdaki çerez ile formdan gelen token değerini karşılaştıracak ve yalnızca her iki veri de uyuştuğunda işlemin devam etmesine izin verecektir.
Modern Yaklaşım: React ve SPA Dünyasında CSRF
Geleneksel EJS yapısında sunucu, HTML sayfasını oluştururken token'ı doğrudan içine gömebilir. Ancak React veya Vue gibi modern bir SPA (Single Page Application) mimarisinde sunucunuz artık HTML değil, yalnızca ham veri (JSON) döndürür. Bu mimari değişim, "gizli input" mantığını devre dışı bırakırken, yerini çok daha esnek bir yöntem olan HTTP Header (Başlık) tabanlı doğrulamaya bırakır.
SPA Mimarisinde İş Akışı Nasıl Değişir?
Modern teknolojilerde süreç genellikle şu şekilde ilerler:
Token İsteği: React uygulaması başladığında veya kullanıcı giriş yaptığında, sunucudan bir CSRF token talep eder (Genellikle bir GET isteği sonucunda JSON olarak veya HTTP-Only olmayan bir çerez içinde gelir).
Header ile Gönderim: Frontend tarafı (örneğin Axios kullanarak), sunucuya yapacağı tüm
POST/PUT/DELETEisteklerinin başlığına (genellikleX-CSRF-Tokenismiyle) bu anahtarı ekler.Sunucu Tarafında Doğrulama: SPA mimarisinde sunucu tarafındaki CSRF ara yazılımı (middleware), gelen isteğin gövdesine (body) bakmak yerine HTTP başlıklarındaki (header) özel anahtarı kontrol eder. Geleneksel yapıdaki
csurfgibi paketlerin modern alternatifleri, isteğin içindekiX-CSRF-Tokenbaşlığı ile sunucuda saklanan gizli anahtarı karşılaştırır. Eğer başlık eksikse veya değerler uyuşmuyorsa, sunucu işlemi reddederek "403 Forbidden" hatası döner. Bu sayede, API endpoint'leriniz sadece sizin frontend uygulamanızdan gelen "doğrulanmış" isteklere cevap verir.
Buradaki en büyük koruma kalkanı Tarayıcı Güvenliğidir. Geleneksel yöntemde sunucuya güveniyorduk, burada ise tarayıcının Same-Origin Policy (SOP) ve CORS kurallarına güveniyoruz. Saldırgan, kullanıcının çerezlerini 'otomatik' olarak göndertebilir ama kullanıcının adına 'özel bir başlık (header)' oluşturamaz. Çünkü tarayıcı, başka bir domain'den gelen script'in sizin API'nize özel başlıklar eklemesine izin vermez.
Örnek: React + Axios Entegrasyonu
Backend tarafında token'ı sunduğunuzda, React tarafında yapmanız gereken tek şey bu mührü her isteğe otomatik olarak basmaktır:
Öncelikle, uygulaman genelinde kullanacağın bir Axios instance'ı oluşturmak en temiz yoldur.
// api/client.js
import axios from 'axios';
const apiClient = axios.create({
baseURL: 'https://api.ecakir.dev',
withCredentials: true, // Çerezlerin (Session ID) gönderilmesi için şart!
});
// 1. Sunucudan CSRF Token'ı alan fonksiyon
export const fetchCsrfToken = async () => {
const { data } = await apiClient.get('/api/csrf-token');
return data.csrfToken;
};
// 2. Axios Request Interceptor: Her isteği yola çıkmadan önce yakalar
apiClient.interceptors.request.use((config) => {
// Sadece veriyi değiştiren metodlara (POST, PUT, DELETE) token ekliyoruz
const sensitiveMethods = ['post', 'put', 'delete', 'patch'];
if (sensitiveMethods.includes(config.method.toLowerCase())) {
// SessionStorage veya bir state'ten token'ı okuyup header'a ekliyoruz
const token = window.sessionStorage.getItem('csrfToken');
if (token) {
config.headers['X-CSRF-Token'] = token;
}
}
return config;
}, (error) => {
return Promise.reject(error);
});
export default apiClient;// App.js
useEffect(() => {
const setupSecurity = async () => {
try {
const token = await fetchCsrfToken(); // Sunucudan taze token'ı al
window.sessionStorage.setItem('csrfToken', token); // Kasaya koy
} catch (err) {
// Eğer burada hata alıyorsak, sunucuyla bir bağlantı veya CORS sorunu olabilir.
console.error("Güvenlik protokolü başlatılamadı.");
}
};
setupSecurity();
}, []);// AddProduct.js
const handleSubmit = async (formData) => {
// Burada 'apiClient' kullanıyoruz, standart axios değil!
// Interceptor arka planda sessionStorage'daki token'ı otomatik ekleyecek.
await apiClient.post('/products', formData);
};Sonuç: Güvenlik Bir Tercih Değil, Standarttır
Web'in doğuştan gelen "unutkanlık" sorununu Cookies ve Sessions ile aştık; ancak bu çözümün getirdiği güvenlik risklerini de Hashing ve CSRF Token ile doğruladık. İster geleneksel EJS şablonları kullanın, ister modern bir React uygulaması geliştirin; kimlik doğrulama sisteminizin güvenliği, bu görünmez ama hayati koruma katmanlarına bağlıdır.
Bir Sonraki Konu: XSS (Cross-Site Scripting) CSRF ile dışarıdan gelen hayalet saldırganları engelledik ve tarayıcının güvenlik bariyerlerini (SOP/CORS) kendi lehimize kullandık. Peki ya saldırgan doğrudan sitemizin içine sızıp kendi kodunu çalıştırırsa? Bir sonraki yazımda, sessionStorage ve cookie'lerin en büyük düşmanı olan XSS saldırılarını ve bunlardan korunma yollarını teknik olarak inceleyeceğiz.