CSP bir XSS açığını otomatik olarak düzeltmez. W3C spesifikasyonu CSP’yi savunma derinliği katmanı olarak tanımlar; güvenli çıktı kodlama, bağlama duyarlı kaçış, girdi doğrulama, güvenli DOM API’leri ve düzenli güvenlik testleri yine gereklidir. Güçlü bir politika, uygulamada enjeksiyon hatası bulunsa bile saldırganın betik çalıştırmasını zorlaştırır.
Bu rehber, bir siteyi doğrudan katı politikaya geçirip bozmak yerine varlık envanteri, Content-Security-Policy-Report-Only, nonce veya hash tabanlı Strict CSP, ihlal raporlama ve kademeli zorlamadan oluşan güvenli uygulama akışını anlatır.
CSP hangi sorunları azaltır?
CSP özellikle şu saldırı ve yanlış yapılandırma sınıflarında ek koruma sağlar:
- HTML içine enjekte edilen kötü amaçlı JavaScript’in çalıştırılması
- Güvenilmeyen alanlardan betik, stil, yazı tipi, medya veya çerçeve yüklenmesi
- Form verilerinin saldırganın alan adına gönderilmesi
- Sayfanın kötü amaçlı bir iframe içine yerleştirilmesi ve clickjacking
- Belge kök adresinin base etiketiyle değiştirilmesi
- Flash veya benzeri eski eklenti nesnelerinin yüklenmesi
- Beklenmedik fetch, XHR, WebSocket veya beacon bağlantıları
- HTTP kaynaklarının HTTPS sayfaya karışık içerik olarak eklenmesi
Politikanın etkisi kullandığınız direktiflere bağlıdır. Yalnızca default-src 'self' yazmak başlangıç sağlar; ancak form hedefi, iframe yerleştirme, bağlantılar ve betik güveni için ayrı kararlar gerekir.
CSP nasıl teslim edilir?
Tercih edilen yöntem HTTP yanıt başlığıdır:
CODE TERMINAL
Content-Security-Policy: default-src 'self'; object-src 'none'; base-uri 'none'Test aşamasında aynı politika engelleme yapmadan rapor toplamak için şu başlıkla gönderilir:
CODE TERMINAL
Content-Security-Policy-Report-Only: default-src 'self'; object-src 'none'; base-uri 'none'; report-to csp-endpointHTML meta etiketi de sınırlı bir seçenek sunar:
CODE TERMINAL
Ancak meta yöntemi politikanın tüm özelliklerini desteklemez. Özellikle raporlama ve frame-ancestors gibi bazı direktifler için HTTP başlığı kullanılmalıdır. Ayrıca meta etiketi belgenin mümkün olduğunca erken bölümünde bulunmalı; ondan önce yüklenen kaynaklara geriye dönük uygulanmaz.
Temel CSP direktifleri
| Direktif | Kontrol ettiği alan | Tipik başlangıç |
|---|---|---|
| default-src | Ayrı direktifi bulunmayan kaynak türleri için geri dönüş | 'self' |
| script-src | JavaScript kaynakları ve çalıştırma kuralları | nonce veya hash tabanlı politika |
| style-src | Stil dosyaları ve satır içi stiller | 'self' ve gerektiğinde nonce |
| img-src | Görseller | 'self' data: ve gerekli CDN |
| font-src | Web yazı tipleri | 'self' ve gerekli font alanları |
| connect-src | fetch, XHR, WebSocket, EventSource ve beacon | 'self' ve açık API uçları |
| frame-src | Sayfanın yükleyebileceği iframe kaynakları | gereken alanlar veya 'none' |
| frame-ancestors | Sayfayı kimlerin iframe içine alabileceği | 'none' veya 'self' |
| form-action | Formların gönderilebileceği hedefler | 'self' |
| base-uri | base etiketinin izinli adresleri | 'none' |
| object-src | object ve embed eklentileri | 'none' |
| worker-src | Worker ve Service Worker kaynakları | 'self' |
| manifest-src | Web uygulama manifesti | 'self' |
| upgrade-insecure-requests | HTTP alt kaynaklarını HTTPS’e yükseltme isteği | Değer almaz |
| report-to | Reporting API uç nokta grubunu seçer | csp-endpoint |
default-src bir geri dönüş direktifidir; bütün alanları kapsayan kalıtım mekanizması değildir. Örneğin frame-ancestors, form-action ve base-uri için açık politika yazmak daha güvenlidir.
Allowlist CSP neden tek başına zayıf kalabilir?
Eski CSP uygulamalarında script-src alan listesiyle oluşturulur:
CODE TERMINAL
script-src 'self' https://cdn.example.com https://scripts.example.netBu yaklaşım basit görünse de büyük CDN’ler, JSONP uçları, kullanıcı tarafından yüklenebilen dosyalar veya yönlendirmeler politika atlatma yolları oluşturabilir. Liste büyüdükçe hangi alanın gerçekten güvenli kod sunduğunu yönetmek zorlaşır.
Modern öneri, mümkün olduğunda kaynağın bulunduğu alan yerine çalıştırılmasına açıkça izin verilen betiği güvenilir kılan nonce veya hash tabanlı Strict CSP kullanmaktır.
Nonce tabanlı Strict CSP
Nonce, her HTTP yanıtı için kriptografik olarak rastgele üretilen ve yalnızca o yanıtta kullanılan değerdir. Sunucu aynı değeri hem CSP başlığına hem çalışmasına izin verilen script etiketlerine ekler.
Örnek başlık:
CODE TERMINAL
Content-Security-Policy: script-src 'nonce-RESPONSE_RANDOM' 'strict-dynamic'; object-src 'none'; base-uri 'none'Örnek HTML:
CODE TERMINAL
Tarayıcı nonce değeri uyuşmayan satır içi ve haricî betikleri engeller. Güvenlik için nonce:
- Her HTTP yanıtında yeniden üretilmeli.
- Tahmin edilemeyecek kadar güçlü rastgele baytlardan oluşturulmalı.
- Kullanıcı girdisine veya sabit oturum kimliğine dayanmamalı.
- Günlüğe, hata sayfasına ve istemci tarafı telemetriye gereksiz yere yazılmamalı.
- Yalnızca gerçekten güvenilen script ve gerektiğinde style etiketlerine eklenmeli.
Aynı nonce’u bütün gün, bütün kullanıcılar veya bütün oturum boyunca kullanmak “number used once” tasarımını bozar. Saldırgan değeri elde ederse sonraki yanıtlarda yeniden kullanabilir.
PHP ile güvenli nonce üretimi
PHP’de her istek için random_bytes kullanılabilir:
CODE TERMINAL
Nonce değerinin HTML’e yazılması güvenli betik etiketini işaretlemek içindir. Ancak şablonda saldırgan denetimli HTML parçalarına nonce eklenmemeli. Kullanıcı içeriğini string birleştirmeyle script etiketine dönüştürmek CSP’nin güven modelini bozar.
Hash tabanlı Strict CSP
Statik HTML’de her yanıt için nonce üretmek mümkün değilse betiğin içeriği SHA-256, SHA-384 veya SHA-512 ile hash’lenebilir:
CODE TERMINAL
Content-Security-Policy: script-src 'sha256-BASE64_HASH' 'strict-dynamic'; object-src 'none'; base-uri 'none'Tarayıcı, satır içi betiğin baytlarını hash’ler ve başlıktaki değerle karşılaştırır. Tek bir boşluk veya satır sonu değişikliği hash’i geçersiz kılar. Bu nedenle hash tabanlı politika derleme hattında otomatik üretilmeli ve içerikle aynı sürümde dağıtılmalıdır.
Haricî script için CSP hash’i kullanılıyorsa script etiketinde Subresource Integrity değeri de tutarlı biçimde belirtilmelidir:
CODE TERMINAL
Nonce dinamik sunucu tarafı sayfalar için, hash ise değişmeyen statik betikler için daha kolay yönetilir. Aynı uygulamanın farklı bölümlerinde iki yöntem birlikte kullanılabilir.
strict-dynamic ne yapar?
strict-dynamic, geçerli nonce veya hash taşıyan güvenilir bir kök betiğin dinamik olarak yüklediği diğer betiklere güveni aktarır. Bu özellik modern paket yükleyicileri ve üçüncü taraf kod zincirlerinde uzun alan listelerini azaltabilir.
Ancak trust zinciri dikkatle değerlendirilmelidir. Güvenilir kök betik, saldırgan denetimli bir URL’yi script src olarak DOM’a ekliyorsa strict-dynamic bu tehlikeli davranışı güvenilir sayabilir. Bu nedenle güvenilir betiklerde DOM XSS, URL ayrıştırma ve dinamik yükleme güvenliği ayrıca incelenmeli.
strict-dynamic bulunan modern tarayıcılarda script-src içindeki 'self' ve host allowlist değerlerinin bir kısmı göz ardı edilir. Geçiş politikası oluştururken gerçek tarayıcı davranışı laboratuvar ve otomatik testlerle doğrulanmalıdır.
unsafe-inline ve unsafe-eval neden risklidir?
unsafe-inline, satır içi script bloklarına ve olay işleyicilerine geniş izin verir. Bu, saldırganın enjekte ettiği kodun da çalışmasına yol açabileceği için CSP’nin XSS azaltma değerini ciddi biçimde düşürür.
unsafe-eval; eval, Function constructor ve string tabanlı zamanlayıcılar gibi metni kod olarak çalıştıran mekanizmalara izin verir. Eski kütüphaneler bunu isteyebilir; kalıcı çözüm kütüphaneyi güncellemek veya kodu refactor etmektir.
Şu dönüşümler uygulanabilir:
| Riskli örüntü | Güvenli yaklaşım |
|---|---|
| onclick="save()" | JavaScript dosyasında addEventListener kullanmak |
| eval(userValue) | Açık veri ayrıştırma ve izinli işlem eşlemesi |
| setTimeout("run()", 1000) | setTimeout(run, 1000) |
| Satır içi büyük script | Haricî sürümlü dosya veya nonce/hash |
| Dinamik script URL’si | Sabit izinli modül haritası |
Satır içi stil için geçici olarak unsafe-inline eklemek yaygındır; fakat style-src-attr ve style-src-elem ayrımı, nonce tabanlı style etiketleri ve CSS refactor ile kapsam daraltılabilir.
Report-Only ile kırmadan geçiş
Mevcut bir siteye doğrudan zorlayıcı CSP eklemek analitik, ödeme, CAPTCHA, font, harita, WebSocket ve kullanıcı arayüzü bileşenlerini bozabilir. Güvenli geçiş şu sırayla yapılır:
- Sayfanın yüklediği script, style, image, font, frame, worker ve bağlantı hedeflerini envantere alın.
- Hedef politikayı Content-Security-Policy-Report-Only başlığıyla gönderin.
- Raporları ortam, sürüm, direktif ve kaynak türüne göre sınıflandırın.
- Meşru kaynakları en dar kapsamla tanımlayın; gereksiz üçüncü tarafları kaldırın.
- Satır içi scriptleri nonce, hash veya haricî dosyaya taşıyın.
- Otomatik tarayıcı testlerinde kritik kullanıcı akışlarını çalıştırın.
- Önce düşük trafik veya çalışan grubu üzerinde zorlayıcı politikayı etkinleştirin.
- Gevşek enforced politika ile daha katı Report-Only politikasını bir süre birlikte çalıştırın.
- İhlal ve hata oranı kabul edilebilir olduğunda katı politikayı yaygınlaştırın.
Report-Only güvenlik sağlamaz; engelleme yapmadan görünürlük üretir. Bu aşamanın aylarca kalıcı hâle gelmemesi için sorumlu ekip, hedef tarih ve ölçülebilir geçiş kriteri belirlenmeli.
CSP ihlal raporlama
Modern CSP Level 3, report-to direktifini Reporting API yapılandırmasıyla kullanır. Geriye uyumluluk için report-uri bir süre birlikte tutulabilir.
CODE TERMINAL
Reporting-Endpoints: csp-endpoint="https://reports.example.com/csp"
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'nonce-RANDOM' 'strict-dynamic'; object-src 'none'; base-uri 'none'; report-to csp-endpoint; report-uri https://reports.example.com/csp-legacyRapor uç noktası internete açık olduğu için güvenilmeyen veri alır. Uygulama:
- İstek gövdesi boyutunu ve hızını sınırlandırmalı.
- Kimlik doğrulaması olmayan raporları güvenlik kanıtı değil telemetri olarak görmeli.
- Rapor içeriğini HTML arayüzünde güvenli biçimde kodlamalı.
- URL sorgularında bulunabilecek kişisel veya hassas verileri maskelemeli.
- Tekrarlanan raporları örneklemeli ve birleştirmeli.
- İhlalleri uygulama sürümü ve dağıtım zamanı ile ilişkilendirmeli.
Tarayıcı uzantıları, yerel güvenlik ürünleri ve kullanıcı tarafından enjekte edilen betikler gürültü üretebilir. Önceliklendirme, aynı blocked-uri ve violated-directive birleşiminin gerçek kullanıcı akışlarında ne sıklıkla görüldüğüne dayanmalı.
Nginx CSP yapılandırması
Statik veya hash tabanlı bir politika Nginx üzerinde add_header ile eklenebilir:
CODE TERMINAL
add_header Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; object-src 'none'; base-uri 'none'; form-action 'self'; frame-ancestors 'none'" always;Nginx’in rastgele ve her yanıt için güvenilir nonce üretmesi standart yapılandırmada kolay değildir. Dinamik nonce gerekiyorsa uygulama katmanında üretmek veya güvenli bir edge işlevi kullanmak daha uygundur. Sabit değişkeni nonce gibi kullanmak güvenli değildir.
Alt location bloklarında add_header davranışının kalıtımı dikkatle test edilmeli. Hata yanıtları ve yönlendirmeler dâhil beklenen bütün yanıtların doğru başlığı taşıdığı curl ve tarayıcı testleriyle doğrulanmalı.
Apache CSP yapılandırması
Apache mod_headers ile Report-Only başlangıcı:
CODE TERMINAL
Header always set Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src 'self'; object-src 'none'; base-uri 'none'; form-action 'self'; frame-ancestors 'none'"Dinamik nonce uygulama tarafından üretiliyorsa başlığın da aynı uygulama yanıtında oluşturulması daha tutarlıdır. Proxy ve önbellek katmanları kullanıcıya başka yanıtın nonce değerini sunmamalı. HTML önbelleğe alınıyorsa CSP başlığı ve belge birlikte aynı cache anahtarına bağlı olmalıdır.
Örnek üretim politikası
Aşağıdaki politika bir şablondur; alan adları ve özellikler uygulamanın gerçek ihtiyaçlarına göre düzenlenmelidir:
CODE TERMINAL
Content-Security-Policy:
default-src 'self';
script-src 'nonce-{RANDOM}' 'strict-dynamic';
style-src 'self' 'nonce-{RANDOM}';
img-src 'self' data: https://images.example-cdn.com;
font-src 'self';
connect-src 'self' https://api.example.com wss://realtime.example.com;
frame-src https://payments.example-provider.com;
frame-ancestors 'none';
form-action 'self';
base-uri 'none';
object-src 'none';
worker-src 'self';
manifest-src 'self';
upgrade-insecure-requests;
report-to csp-endpoint;Ödeme sağlayıcısı örneği bir tavsiye değil yer tutucudur. Gerçek uygulamada yalnızca kullanılan ve sözleşmeyle doğrulanan alanlar eklenmeli. Joker alanlar mümkün olduğunca kullanılmamalı.
CSP testleri nasıl yapılır?
Politika yalnızca ana sayfada gözle kontrol edilmemeli. Otomatik test matrisi şu akışları kapsamalıdır:
- Giriş, kayıt, parola sıfırlama ve MFA
- Arama, filtre, dosya yükleme ve indirme
- Ödeme, CAPTCHA, harita, canlı destek ve analitik
- SPA yönlendirmeleri, lazy loading ve code splitting
- WebSocket, Service Worker ve Web Worker
- Hata sayfaları, 404, 500 ve bakım modu
- Yönetim paneli ve farklı kullanıcı rolleri
- Mobil tarayıcılar ve desteklenen eski tarayıcı sürümleri
HTTP başlığını görmek için:
CODE TERMINAL
curl -I https://example.com/Tarayıcı geliştirici konsolunda CSP ihlalleri incelenebilir. Otomatik tarayıcı testinde console error olayları toplanarak yeni dağıtımın beklenmeyen engellemeleri CI/CD hattını durdurabilir.
Sık yapılan CSP hataları
- Politikayı yalnızca ana sayfaya uygulamak; hata ve alt yol yanıtlarını kapsamamak
- Sabit nonce kullanmak veya nonce’u oturum boyunca yeniden kullanmak
- Geniş CDN ve joker alan listeleriyle yanlış güven duygusu oluşturmak
- unsafe-inline ve unsafe-eval değerlerini kalıcı çözüm olarak bırakmak
- Report-Only raporlarını toplamak ama zorlayıcı politikaya hiç geçmemek
- frame-ancestors yerine yalnızca frame-src kullanmak
- form-action ve base-uri direktiflerini atlamak
- Rapor uç noktasında boyut, hız ve veri maskeleme kontrollerini unutmamak
- CSP’yi çıktı kodlama ve XSS düzeltmelerinin yerine geçirmek
- Önbellekte CSP başlığı ile HTML nonce değerini birbirinden ayırmak
- Birden fazla proxy katmanının çelişkili CSP başlıkları eklemesine izin vermek
Bir yanıtta birden fazla zorlayıcı CSP başlığı bulunursa tarayıcı politikaları birlikte uygular; sonuç beklenenden daha kısıtlayıcı olabilir. Bu durum çoğu zaman CDN, ters proxy ve uygulamanın ayrı ayrı başlık eklemesinden kaynaklanır.
CSP ile diğer güvenlik başlıkları
CSP tek başına bütün tarayıcı güvenliğini kapsamaz. Aşağıdaki başlıklar ayrı amaçlar taşır:
| Başlık | Amaç |
|---|---|
| Strict-Transport-Security | Tarayıcıyı HTTPS kullanmaya zorlamak |
| X-Content-Type-Options: nosniff | MIME türü tahminini sınırlamak |
| Referrer-Policy | Dış isteklere gönderilen referrer bilgisini azaltmak |
| Permissions-Policy | Kamera, mikrofon, konum gibi tarayıcı yeteneklerini sınırlamak |
| Cross-Origin-Opener-Policy | Pencere bağlamlarını origin sınırında ayırmak |
| Cross-Origin-Resource-Policy | Kaynakların başka originlerden yüklenmesini kontrol etmek |
| Cross-Origin-Embedder-Policy | Sayfanın cross-origin kaynak gömme modelini sıkılaştırmak |
frame-ancestors modern clickjacking kontrolüdür; eski tarayıcı gereksinimleri varsa X-Frame-Options geçiş amacıyla birlikte tutulabilir. Başlıkların birleşik etkisi test edilmeden üretime alınmamalı.
Kademeli CSP uygulama planı
- Hafta 1 – Envanter: Kaynak, üçüncü taraf, inline kod ve kritik akış listesini çıkarın.
- Hafta 2 – Gözlem: Temel Report-Only politikayı ve rapor toplama altyapısını etkinleştirin.
- Hafta 3 – Refactor: Inline event handler, eval ve gereksiz üçüncü tarafları kaldırın; nonce/hash entegrasyonunu tamamlayın.
- Hafta 4 – Pilot: Katı politikayı çalışan veya düşük trafik grubunda enforced olarak dağıtın.
- Hafta 5 – Yaygınlaştırma: Kritik kullanıcı akışlarını otomatik testlerle doğrulayarak trafik oranını artırın.
- Sürekli – İzleme: Yeni kaynak, yeni ihlal ve politika gevşetme değişikliklerini kod incelemesine bağlayın.
Her allowlist eklemesi değişiklik kaydında gerekçe, sahibi ve son kullanma tarihiyle tutulabilir. Böylece geçici izinlerin kalıcı ve unutulmuş güven açıklarına dönüşmesi önlenir.
Sonuç
Content Security Policy, tarayıcıda hangi kaynakların yüklenebileceğini ve hangi kodun çalışabileceğini sınırlandıran güçlü bir savunma derinliği kontrolüdür. Etkili CSP; geniş alan listeleri yerine nonce veya hash tabanlı Strict CSP, object-src 'none', base-uri 'none', form-action ve frame-ancestors gibi açık direktiflerle kurulmalıdır.
En güvenli geçiş, varlık envanteri ve Report-Only gözlemiyle başlar; inline kod refactor edilir, her yanıta özel nonce veya statik hash uygulanır, kritik akışlar otomatik test edilir ve zorlayıcı politika kademeli olarak yaygınlaştırılır. CSP, XSS düzeltmelerinin yerine geçmez; doğru çıktı kodlama ve güvenli DOM kullanımıyla birlikte saldırının etkisini azaltır.
Kaynaklar
- W3C – Content Security Policy Level 3
- MDN – Content Security Policy rehberi
- MDN – Pratik CSP uygulama rehberi
- OWASP Cheat Sheet Series – Content Security Policy
- web.dev – Strict CSP ile XSS etkisini azaltma
TR Siber Ekibi