CSP Nedir? 30 Dakikada XSS’e Kalkan Kurun
CSP (Content Security Policy) ile XSS riskini azaltın. Nonce, hash ve report-only ile adım adım güvenli politika oluşturun.
CSP Nedir? 30 Dakikada XSS’e Kalkan Kurun
Meta Description
CSP nedir ve nasıl uygulanır? XSS saldırılarını azaltmak için nonce/hash, report-only ve güvenli header ayarlarını 30 dakikada öğrenin.
Giriş (Introduction)
CSP (Content Security Policy), modern web uygulamalarında XSS (Cross-Site Scripting) riskini ciddi şekilde azaltan en etkili savunma katmanlarından biridir. “Kodum temiz, XSS olmaz” demek çoğu zaman yetmez; üçüncü parti script’ler, yanlış yapılandırılmış inline kodlar, hatalı HTML render süreçleri bir gün mutlaka kapınızı çalar.
Bu yazıda CSP’yi sadece “teoride” anlatmayacağız: Hangi header’ı nasıl yazarsınız, nonce/hash ne işe yarar, report-only ile nasıl güvenle geçiş yaparsınız adım adım göreceksiniz. 30 dakika sonunda uygulamanızın güvenlik duruşu belirgin şekilde iyileşir.
CSP Nedir? (Content Security Policy)
CSP, tarayıcıya “Bu sayfada hangi kaynaklar (script, style, img, font, iframe vb.) nereden yüklenebilir?” diye kural seti veren bir güvenlik politikasıdır.
CSP sayesinde:
- Beklenmedik bir script enjekte edilse bile tarayıcı çalıştırmayı reddedebilir.
- Sadece izin verdiğiniz domain’lerden kaynak çekilir.
- İhlaller raporlanarak görünür hale gelir.
Bunu neden yapmalıyım? Çünkü XSS’in maliyeti sadece “bir açık” değildir: kullanıcı oturum çalma, ödeme akışlarını manipüle etme, admin panel ele geçirme gibi sonuçlara gider. CSP, hatayı tamamen yok etmese de etkisini dramatik biçimde düşürür.
CSP’nin Temel Direktifleri (Kısa Harita)
Aşağıdaki tablo, günlük hayatta en çok kullanılan CSP direktiflerini özetler:
| Direktif | Ne kontrol eder? | Sık kullanım |
|---|---|---|
default-src |
Varsayılan kaynak politikası | “Hiçbir şey izinli olmasın” yaklaşımı |
script-src |
JS kaynakları | XSS’e karşı ana direktif |
style-src |
CSS kaynakları | Inline style’ı yönetmek |
img-src |
Görseller | CDN, data URL ihtiyacı |
connect-src |
XHR/fetch/WebSocket | API domain’leri |
font-src |
Font kaynakları | Google Fonts / self-hosted |
frame-src |
iframe kaynakları | Ödeme sağlayıcı iframe’leri |
base-uri |
<base> etiketini sınırlar |
URL manipülasyonunu azaltır |
object-src |
Flash vb. | Genelde none |
LSI (ilişkili) terimler: XSS koruması, security header, nonce, hash, report-only, CORS, clickjacking, güvenlik politikası.
1) Hızlı Başlangıç: Minimum “Güvenli” CSP
İlk hedef: varsayılanı kapat, sadece gerekeni aç.
Örnek temel politika:
Content-Security-Policy: default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'
Açıklama:
default-src 'self': Sadece kendi origin’inizden kaynak.object-src 'none': Eski/tehlikeli embed’leri kapatır.base-uri 'self':<base>ile yönlendirme/manipülasyonu kısıtlar.frame-ancestors 'none': Sitenizin iframe içinde açılmasını engeller (clickjacking’e karşı).
Gerçek hayat örneği: Admin paneliniz bir başka sitede iframe içinde açılırsa, kullanıcı tıklamalarını başka yerlere yönlendiren clickjacking senaryoları doğabilir.
frame-ancestorsbunu keser.
2) En Kritik Kısım: script-src ve Inline Script Sorunu
Çoğu projede problem şudur: HTML içinde inline script vardır (örn. analytics init, config objesi, küçük snippet’ler). CSP ile bunu “serbest bırakırsanız” ('unsafe-inline') XSS savunmasını zayıflatırsınız.
Seçenek A: Nonce ile güvenli inline script
Her request’te rastgele bir nonce üretir, sadece o nonce’a sahip script’lerin çalışmasına izin verirsiniz.
CSP header:
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-<RANDOM>'; object-src 'none'; base-uri 'self'
HTML:
<script nonce="<RANDOM>">
window.__APP_CONFIG__ = { env: "prod" };
</script>
Seçenek B: Hash ile belirli inline script’e izin
Inline script içeriği değişmiyorsa hash kullanabilirsiniz.
Content-Security-Policy: script-src 'self' 'sha256-Base64EncodedHash...'
Bunu neden yapmalıyım? Çünkü
unsafe-inlinekullanmak, “sayfaya sızan her inline script çalışabilir” anlamına gelir. Nonce/hash, inline ihtiyacını güvenli hale getirir.
3) Report-Only ile Kırmadan Geçiş (Önerilen Yol)
CSP’yi bir anda “en sıkı” hale getirirseniz production’da bazı sayfalarınız bozulabilir (özellikle üçüncü parti script’ler, CDN’ler, fontlar, ödeme sayfaları).
Bu yüzden önce:
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; report-to csp-endpoint
Raporlamayı nasıl okurum?
- Browser console’da CSP violation uyarıları görünür.
- Gerçek kullanıcı trafiğinden gelen ihlalleri toplayıp izin listesini doğru kurarsınız.
Pratik: 3–7 gün report-only çalıştırın, log’ları inceleyin, ardından gerçek
Content-Security-Policyheader’ına geçin.
4) Node.js (Express) ile CSP Header’ı Eklemek
Aşağıdaki örnek, nonce üreterek her response’a CSP ekler.
import crypto from "crypto";
import express from "express";
const app = express();
app.use((req, res, next) => {
const nonce = crypto.randomBytes(16).toString("base64");
res.locals.nonce = nonce;
res.setHeader(
"Content-Security-Policy",
[
"default-src 'self'",
`script-src 'self' 'nonce-${nonce}'`,
"style-src 'self'",
"img-src 'self' data:",
"connect-src 'self' https://api.example.com",
"object-src 'none'",
"base-uri 'self'",
"frame-ancestors 'none'"
].join("; ")
);
next();
});
app.get("/", (req, res) => {
// Template motorunuzda nonce'u script tag'ine basın
res.send(`
<html>
<head></head>
<body>
<script nonce="${res.locals.nonce}">
console.log('CSP nonce ile çalıştı');
</script>
</body>
</html>
`);
});
app.listen(3000);
Yaygın hata
- Nonce’ı ürettiniz ama HTML’de script tag’ine basmadınız → script’ler çalışmaz.
5) CDN, Analytics ve Üçüncü Parti Script’ler: Nasıl İzin Verilir?
Gerçek hayatta çoğu ekip şu ikilemde kalır: “Güvenlik mi, pazarlama araçları mı?”
Örnek bir senaryo:
- Uygulama:
self - Analytics:
https://www.googletagmanager.com - Font:
https://fonts.gstatic.com
Örnek politika (temkinli):
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-<RANDOM>' https://www.googletagmanager.com;
connect-src 'self' https://analytics.google.com;
style-src 'self' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' data: https://www.google-analytics.com;
object-src 'none';
base-uri 'self';
frame-ancestors 'none'
İpucu:
- Gereksiz wildcard kullanmayın:
https://*.example.combazen kaçınılmazdır ama önce net domain’lerle başlayın. connect-srcgenelde unutulur; API çağrıları ve telemetry burada patlar.
6) CSP Checklist: Production’a Çıkmadan Önce
Aşağıdaki liste, “bozmadan sıkılaştırma” için pratik bir yol haritası:
- Report-Only ile başlayın (
Content-Security-Policy-Report-Only). - Console ve log’lardan ihlalleri toplayın.
default-src 'self'+object-src 'none'+base-uri 'self'tabanını kurun.script-srciçin nonce (tercihen) kullanın.frame-ancestorsile clickjacking’i kapatın.- CDN/3rd party domain’lerini minimum seviyede izinleyin.
- Policy’yi kademeli sıkılaştırın (ör.
img-src data:gerçekten gerekli mi?).
Sık Sorulan Sorular (FAQ)
1) CSP XSS’i tamamen engeller mi?
Hayır. CSP, XSS’in etkisini azaltır ve birçok saldırıyı tarayıcı seviyesinde durdurur; yine de input validation ve output encoding şarttır.
2) unsafe-inline kullanmak zorunda mıyım?
Çoğu durumda hayır. Inline script ihtiyacını nonce veya hash ile çözebilirsiniz. unsafe-inline en son çare olmalı.
3) CSP ekledim, site bozuldu. Ne yapmalıyım?
Önce Content-Security-Policy-Report-Only ile ilerleyin. İhlal mesajlarına göre eksik domain/direktifleri ekleyin.
4) CSP ile CORS aynı şey mi?
Değil. CORS, tarayıcının cross-origin istekleri nasıl yöneteceğini belirler. CSP ise sayfanın hangi kaynakları yükleyip çalıştırabileceğini kısıtlar.
Sonuç
CSP, özellikle XSS’e karşı “tek başına mucize” olmasa da web uygulamalarında en hızlı değer üreten güvenlik katmanlarından biridir. Nonce/hash yaklaşımıyla inline script’leri kontrol altına alır, default-src ve script-src ile saldırı yüzeyini daraltırsınız.
Bir sonraki adım: Uygulamanıza Report-Only CSP ekleyin, 3 gün ihlalleri toplayın ve ardından gerçek CSP’ye geçin. Deneyiminizi yorumlarda paylaşın: Hangi direktif sizi en çok zorladı, hangi üçüncü parti servisler policy’yi deldi?