Content Security Policy (CSP), tarayıcıya bir web sayfasının hangi kaynakları yükleyebileceğini ve hangi kodları çalıştırabileceğini bildiren HTTP güvenlik politikasıdır. Doğru uygulandığında XSS ve içerik enjeksiyonu açıklarının etkisini azaltır; kötü amaçlı betiklerin, çerçevelerin ve ağ bağlantılarının çalışmasını sınırlar.

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-endpoint


HTML 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

DirektifKontrol ettiği alanTipik başlangıç
default-srcAyrı direktifi bulunmayan kaynak türleri için geri dönüş'self'
script-srcJavaScript kaynakları ve çalıştırma kurallarınonce veya hash tabanlı politika
style-srcStil dosyaları ve satır içi stiller'self' ve gerektiğinde nonce
img-srcGörseller'self' data: ve gerekli CDN
font-srcWeb yazı tipleri'self' ve gerekli font alanları
connect-srcfetch, XHR, WebSocket, EventSource ve beacon'self' ve açık API uçları
frame-srcSayfanın yükleyebileceği iframe kaynaklarıgereken alanlar veya 'none'
frame-ancestorsSayfayı kimlerin iframe içine alabileceği'none' veya 'self'
form-actionFormların gönderilebileceği hedefler'self'
base-uribase etiketinin izinli adresleri'none'
object-srcobject ve embed eklentileri'none'
worker-srcWorker ve Service Worker kaynakları'self'
manifest-srcWeb uygulama manifesti'self'
upgrade-insecure-requestsHTTP alt kaynaklarını HTTPS’e yükseltme isteğiDeğer almaz
report-toReporting API uç nokta grubunu seçercsp-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.net


Bu 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 scriptHaricî sürümlü dosya veya nonce/hash
Dinamik script URL’siSabit 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:


  1. Sayfanın yüklediği script, style, image, font, frame, worker ve bağlantı hedeflerini envantere alın.
  2. Hedef politikayı Content-Security-Policy-Report-Only başlığıyla gönderin.
  3. Raporları ortam, sürüm, direktif ve kaynak türüne göre sınıflandırın.
  4. Meşru kaynakları en dar kapsamla tanımlayın; gereksiz üçüncü tarafları kaldırın.
  5. Satır içi scriptleri nonce, hash veya haricî dosyaya taşıyın.
  6. Otomatik tarayıcı testlerinde kritik kullanıcı akışlarını çalıştırın.
  7. Önce düşük trafik veya çalışan grubu üzerinde zorlayıcı politikayı etkinleştirin.
  8. Gevşek enforced politika ile daha katı Report-Only politikasını bir süre birlikte çalıştırın.
  9. İ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-legacy


Rapor 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ıkAmaç
Strict-Transport-SecurityTarayıcıyı HTTPS kullanmaya zorlamak
X-Content-Type-Options: nosniffMIME türü tahminini sınırlamak
Referrer-PolicyDış isteklere gönderilen referrer bilgisini azaltmak
Permissions-PolicyKamera, mikrofon, konum gibi tarayıcı yeteneklerini sınırlamak
Cross-Origin-Opener-PolicyPencere bağlamlarını origin sınırında ayırmak
Cross-Origin-Resource-PolicyKaynakların başka originlerden yüklenmesini kontrol etmek
Cross-Origin-Embedder-PolicySayfanı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ı


  1. Hafta 1 – Envanter: Kaynak, üçüncü taraf, inline kod ve kritik akış listesini çıkarın.
  2. Hafta 2 – Gözlem: Temel Report-Only politikayı ve rapor toplama altyapısını etkinleştirin.
  3. Hafta 3 – Refactor: Inline event handler, eval ve gereksiz üçüncü tarafları kaldırın; nonce/hash entegrasyonunu tamamlayın.
  4. Hafta 4 – Pilot: Katı politikayı çalışan veya düşük trafik grubunda enforced olarak dağıtın.
  5. Hafta 5 – Yaygınlaştırma: Kritik kullanıcı akışlarını otomatik testlerle doğrulayarak trafik oranını artırın.
  6. 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

TR Siber Ekibi Yazar · X
Kaynak bağlantısı ← Ana sayfaya dön