İçerik Güvenlik Politikası (CSP): XSS’i Son Savunma Hattında Durdurmak
Kaçırdığınız tek bir XSS açığını zararsız kılan bir başlık var. Ama yanlış kurulursa hiçbir şey yapmaz.
İçerik güvenlik politikası (CSP), tarayıcıya "bu sayfada hangi kaynaklardan kod çalıştırılabilir" listesini veren bir HTTP başlığıdır. XSS açığını kapatmaz — onu kapatmak girdi doğrulama ve çıktı kaçırmanın işidir. CSP'nin görevi farklıdır: kaçırdığınız bir açığın sömürülmesini engellemektir. Yani son savunma hattıdır.

Neden gerekli?
Büyük bir uygulamada tek bir kaçış noktası bile yeter. Bir yorum alanı, bir profil biçimi, üçüncü parti bir bileşenin çıktısı — bunlardan biri kaçırılmadan basılırsa saldırgan script çalıştırabilir. CSP doğru kurulduğunda o script, alan dışı bir kaynaktan geldiği için tarayıcı tarafından reddedilir.
Aşama 1: kırmadan gözlemleyin
CSP'yi doğrudan zorlayıcı modda açmak, sitenin yarısını çalışmaz hâle getirmenin en hızlı yoludur. Doğru başlangıç Report-Only modudur: tarayıcı hiçbir şeyi engellemez, yalnızca ihlalleri bildirir.
// app/Filters/CspFilter.php
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
{
$politika = implode('; ', [
"default-src 'self'",
"script-src 'self'",
"style-src 'self' 'unsafe-inline'", // stil için geçici tolerans
"img-src 'self' data: https:",
"font-src 'self' data:",
"connect-src 'self'",
"frame-ancestors 'none'", // clickjacking koruması
"base-uri 'self'",
"form-action 'self'",
'report-uri ' . base_url('guvenlik/csp-rapor'),
]);
// ÖNCE Report-Only: neyin kırılacağını üretimde öğrenin
$response->setHeader('Content-Security-Policy-Report-Only', $politika);
}Aşama 2: raporları toplayın
public function cspRapor()
{
$ham = $this->request->getBody();
$veri = json_decode((string) $ham, true)['csp-report'] ?? null;
if (! is_array($veri)) {
return $this->response->setStatusCode(204);
}
// Gürültüyü azalt: aynı ihlali dakikada bir kez yaz
$anahtar = 'csp:' . md5(($veri['violated-directive'] ?? '') . ($veri['blocked-uri'] ?? ''));
if (! cache()->get($anahtar)) {
cache()->save($anahtar, 1, 60);
log_message('warning', 'CSP ihlali: ' . json_encode([
'yonerge' => $veri['violated-directive'] ?? null,
'engellenen' => $veri['blocked-uri'] ?? null,
'sayfa' => $veri['document-uri'] ?? null,
], JSON_UNESCAPED_UNICODE));
}
return $this->response->setStatusCode(204);
}Rapor ucunu hız sınırlaması olmadan açık bırakmayın. Tarayıcılar ihlal başına istek gönderir; kötü bir politika saniyede binlerce rapor üretebilir ve kendi ucunuz sizi devirir.
Aşama 3: satır içi script sorunu
Gerçek projelerde satır içi <script> blokları vardır. Çözüm 'unsafe-inline' eklemek değildir — bu, CSP'yi tamamen etkisiz kılar, çünkü saldırganın enjekte ettiği script de satır içidir. Doğru çözüm nonce'tur: her yanıtta rastgele bir değer üretilir, hem başlığa hem de meşru script etiketlerine yazılır.
// Filtrenin before() aşamasında istek başına BİR kez üretilir
$nonce = base64_encode(random_bytes(16));
service('request')->setGlobal('csp_nonce', $nonce); // görünümler okuyacak
$politika = "script-src 'self' 'nonce-{$nonce}'";<!-- Görünümde: yalnızca BİZİM script'imiz nonce taşır -->
<script nonce="<?= esc($cspNonce, 'attr') ?>">
console.log('Bu blok çalışır.');
</script>
<!-- Saldırganın enjekte ettiği blok nonce bilemez → tarayıcı çalıştırmaz -->Nonce her yanıtta değişmelidir. Sabit bir nonce, saldırganın onu okuyup kendi etiketine yazmasıyla anlamsızlaşır.
Aşama 4: zorlayıcı moda geçiş
Raporlar birkaç gün boyunca temiz geliyorsa başlık adını Content-Security-Policy olarak değiştirin — ama report-uri yönergesini kaldırmayın. Zorlayıcı modda da raporlama, yeni eklenen bir üçüncü parti bileşenin sessizce kırılmasını fark etmenizi sağlar.
Bu tür kuralları ekibe aktarırken canlı örnek üzerinden ilerlemek etkilidir; teknik mülakatta canlı kod yazma yazımızdaki yaklaşım eğitimlerde de işe yarar.
Sık yapılan üç hata
Her şeye izin veren politika. default-src * ile başlayan bir politika yalnızca güvenlik taramalarında "CSP var" tikini almanızı sağlar; gerçek koruma sıfırdır.
frame-ancestors unutmak. Bu yönerge, sayfanızın başka bir sitede çerçevelenmesini engeller ve clickjacking'e karşı en pratik korumadır. Eski X-Frame-Options başlığının yerini almıştır.
CSP'yi girdi doğrulamanın yerine koymak. CSP bir yedek plandır. Asıl savunma hâlâ çıktı kaçırma ve izin listesine dayalı HTML temizlemedir; en sık görülen açıklar yazımızda bu katmanları sıralamıştık.
Kod paylaşan siteler için özel not
Blog veya dokümantasyon gibi kullanıcı/editör HTML'i basan sistemlerde CSP daha da değerlidir, çünkü zengin metin editörleri geniş bir etiket kümesine izin verir. Kaydetme anında izin listesiyle temizlemek ve yayında CSP ile ikinci bir kapı kurmak birlikte çalışır. Bağımlılık üzerinden gelen riskler içinse bağımlılık zinciri saldırıları yazımıza bakabilirsiniz; CSP, ele geçirilmiş bir CDN betiğinin veri sızdırmasını da connect-src ile sınırlar. Yönergelerin tam listesi için MDN CSP belgeleri kapsamlı bir referanstır. Oturum tarafındaki tamamlayıcı önlemler için parola saklama ve oturum güvenliği yazımız okunabilir.
Sonuç
CSP, doğru kurulduğunda kaçırılan bir XSS açığını sömürülemez hâle getiren tek mekanizmadır; yanlış kurulduğunda ise yalnızca bir güvenlik raporunda iyi görünen bir başlıktır. Farkı yaratan üç karardır: unsafe-inline yazmamak, satır içi script'leri istek başına değişen nonce ile beyaz listelemek ve zorlayıcı moda ancak rapor modunda temiz sonuç aldıktan sonra geçmek. Bugün Report-Only başlığını ekleyip bir hafta rapor toplayın — sitenizin hangi kaynaklardan kod çalıştırdığını görmek, tek başına bile öğretici olacaktır.
Sık Sorulan Sorular
Hayır, CSP yalnızca bir HTTP başlığıdır ve tarayıcı tarafında ihmal edilebilir bir maliyeti vardır. Yavaşlama hissi genelde politikanın bazı kaynakları engellemesi ve sayfanın eksik yüklenmesinden kaynaklanır; bu bir performans değil yapılandırma sorunudur.
Trafiğin haftanın tüm günlerini ve ana kullanıcı akışlarını kapsayacak kadar. Pratikte bir hafta çoğu site için yeterlidir. Nadiren kullanılan yönetim ekranlarını unutmamak için o sayfaları elle de gezmek gerekir.
Evet, satır içi blok sabitse SHA-256 özetini politikaya yazabilirsiniz. Ancak blok her değiştiğinde politikayı güncellemeyi unutmak kolaydır; dinamik içerik üreten sayfalarda nonce daha sürdürülebilirdir.
Her eklenen dış kaynak saldırı yüzeyini büyütür. En azından tam alan adını (joker karakter kullanmadan) yazın ve connect-src ile veri gönderebileceği adresleri sınırlayın. Gerçekten gerekli olmayan üçüncü parti betiği kaldırmak, en etkili güvenlik iyileştirmesidir.
Yorumlar (0)
Bu yazıya henüz yorum yapılmamış. İlk yorumu siz yazın!
Yorum Yaz
Yorumunuz onaylandıktan sonra yayınlanır. Ekibimiz gerekirse konuyla ilgili bir yanıt da paylaşır.