---
title: 'İçerik Güvenlik Politikası'
source: 'https://academia.sh/tr/kurslar/frontend-kalitesi/icerik-guvenlik-politikasi'
course: 'Frontend Kalitesi'
language: tr
updated: '2026-08-17T18:09:22+00:00'
license: 'CC BY-SA 4.0'
---

# İçerik Güvenlik Politikası

Tarayıcıya hangi kaynağın yüklenebileceğini bildiren politikanın yazımı; yönergeler ve kaynak ifadeleri, im ve karma tabanlı izin, rapor kipi ve ihlal kaydı, politikanın kapsamadığı alanlar.

Önceki ders savunmayı çıkışa yerleştirdi: her yazma noktasında bağlama uygun kaçırma.
Bu savunmanın zayıf yanı kapsamıdır. Doğru olması için uygulamadaki her yolun doğru olması
gerekir; tek bir ham işaretleme noktası, tek bir üçüncü taraf bileşen, tek bir eski sayfa
şablonu yeter. Büyük bir arayüzde bu kapsamın sürekli korunacağını varsaymak, denetlenmesi
zor bir varsayımdır.

Bu ders ikinci bir katman kurar. Katmanın niteliği önemlidir: uygulamanın kendi kodunun
doğruluğuna bağlı değildir. Karar tarayıcıya bildirilir ve tarayıcı, belge ne söylerse
söylesin, yükleme anında bu kararı uygular. Kuzey Yamaç Ölçüm İstasyonu arayüzü boyunca
kurulacak politika, ölçüm notunu gösteren sayfanın hangi betikleri çalıştırabileceğini ve
gömülü harita çerçevesine izin verilip verilmeyeceğini bu yolla sabitler.

## Politikanın Yaptığı İş

**İçerik güvenlik politikası (content security policy)**, bir yanıtın
`Content-Security-Policy` başlığıyla taşınan bildirimidir. Bildirim, belgenin hangi
türden kaynağı nereden yükleyebileceğini ve satır içi kodun hangi koşulla
çalıştırılabileceğini sayar. Tarayıcı bir alt kaynağı (subresource) getirmeden veya bir
betiği çalıştırmadan önce politikaya bakar; ifade listesinde eşleşme yoksa işlemi
gerçekleştirmez.

Bu, kaçırmanın yerine geçen bir savunma değildir. Kaçırma verinin koda dönüşmesini
engeller; politika, engelin aşıldığı durumda çalışacak kodun ne yapabileceğini daraltır.
İkisi bağımsız katmanlardır ve bağımsız oldukları için birlikte çalışırlar.

Politika HTTP başlığı olarak gönderilir. Belge içindeki `meta` ögesiyle de bildirilebilir,
ancak bu yol bazı yönergeleri desteklemez ve belge ayrıştırılmaya başladıktan sonra devreye
girer. Sunucu yapılandırmasında başlık olarak tanımlamak, kapsamı geniş olan yoldur.

## Yönergeler ve Kaynak İfadeleri

Politika, noktalı virgülle ayrılmış **yönergelerden (directive)** oluşur. Her yönerge bir
kaynak türünü adlandırır ve arkasından o tür için geçerli **kaynak ifadelerini** listeler.

`default-src` diğer yönergelerin yedeğidir: politika belirli bir tür için yönerge
içermiyorsa tarayıcı bu listeye bakar. `script-src` betikleri, `style-src` biçem
dosyalarını, `img-src` görselleri, `connect-src` betikten açılan ağ bağlantılarını,
`font-src` yazı tiplerini kapsar. Bunların yanında yükleme dışı kararlar veren yönergeler
vardır: `frame-ancestors` belgeyi kimin çerçeveleyebileceğini, `form-action` form
gönderiminin nereye gidebileceğini, `base-uri` belge taban adresinin değiştirilip
değiştirilemeyeceğini belirler.

Kaynak ifadeleri birkaç biçimdedir. `'none'` hiçbir şeye izin vermez. `'self'` belgenin
kendi kaynağını gösterir. Şema ifadesi (`https:`, `data:`) o şemadan gelen her adresi
kapsar. Konak ifadesi bir alan adını, önüne yıldız konarak alt alanlarını da içerir.
`'nonce-…'` ve `'sha256-…'` satır içi kod için tek tek izin verir. `'unsafe-inline'`
satır içi kodun tamamına izin verir ve adındaki uyarı yerindedir: bu ifade, politikanın
betik çalıştırmaya karşı sağladığı korumayı büyük ölçüde geçersiz kılar.

## Politikayı Karara Bağlamak

Politikanın nasıl okunduğunu görmenin en doğrudan yolu, okuyan kodu yazmaktır. Aşağıdaki
çözümleyici bir politika dizgesini yönergelere ayırır, verilen isteği ilgili listeye karşı
sınar ve yedek yönergeye düşme davranışını uygular.

```js
// csp.mjs — politika dizgesini cozumler ve bir kaynagin izinli olup olmadigini karara baglar
function politikayiCozumle(dizge) {
  const yonergeler = new Map();
  for (const parca of dizge.split(';')) {
    const sozcukler = parca.trim().split(/\s+/).filter(Boolean);
    if (sozcukler.length === 0) continue;
    yonergeler.set(sozcukler[0].toLowerCase(), sozcukler.slice(1));
  }
  return yonergeler;
}

function konakEsler(ifade, adres) {
  const [semaKismi, kalan] = ifade.includes('://') ? ifade.split('://') : [null, ifade];
  const [konak, ...yol] = kalan.split('/');
  if (semaKismi && `${semaKismi}:` !== adres.protocol) return false;
  if (konak.startsWith('*.')) {
    if (!adres.hostname.endsWith(konak.slice(1))) return false;
  } else if (konak !== adres.host && konak !== adres.hostname) {
    return false;
  }
  if (yol.length > 0 && yol[0] !== '') return adres.pathname.startsWith('/' + yol.join('/'));
  return true;
}

function ifadeEsler(ifade, istek, belgeKaynagi) {
  if (ifade === "'none'") return false;
  if (ifade === "'self'") return istek.adres !== null && new URL(istek.adres).origin === belgeKaynagi;
  if (ifade === "'unsafe-inline'") return istek.satirIci === true && !istek.nonce;
  if (ifade.startsWith("'nonce-")) return istek.nonce === ifade.slice(7, -1);
  if (ifade.startsWith("'sha256-")) return istek.karma === ifade.slice(1, -1);
  if (ifade.endsWith(':')) return istek.adres !== null && new URL(istek.adres).protocol === ifade;
  return istek.adres !== null && konakEsler(ifade, new URL(istek.adres));
}

function karar(politika, istek, belgeKaynagi) {
  const yonergeler = politikayiCozumle(politika);
  const ad = yonergeler.has(istek.yonerge) ? istek.yonerge : 'default-src';
  const liste = yonergeler.get(ad);
  if (!liste) return { izinli: true, yonerge: null };
  const eslesen = liste.find((ifade) => ifadeEsler(ifade, istek, belgeKaynagi));
  return { izinli: Boolean(eslesen), yonerge: ad, eslesen: eslesen ?? null };
}

function raporKaydi(istek, sonuc, politika, kip) {
  return {
    'document-uri': 'https://olcum.ornek.test/istasyon/kuzey-yamac',
    'violated-directive': sonuc.yonerge,
    'blocked-uri': istek.adres ?? 'inline',
    'disposition': kip,
    'original-policy': politika,
  };
}

const POLITIKA =
  "default-src 'none'; script-src 'self' 'nonce-r4k2' https://olcum-cdn.ornek.test; " +
  "img-src 'self' data:; connect-src 'self' https://*.veri.ornek.test; frame-ancestors 'none'";
const KAYNAK = 'https://olcum.ornek.test';

const istekler = [
  { ad: 'kendi betigi', yonerge: 'script-src', adres: 'https://olcum.ornek.test/js/olcum.js' },
  { ad: 'dagitim betigi', yonerge: 'script-src', adres: 'https://olcum-cdn.ornek.test/grafik.js' },
  { ad: 'yabanci betik', yonerge: 'script-src', adres: 'https://baska.ornek.test/izleme.js' },
  { ad: 'imli satir ici', yonerge: 'script-src', adres: null, satirIci: true, nonce: 'r4k2' },
  { ad: 'imsiz satir ici', yonerge: 'script-src', adres: null, satirIci: true },
  { ad: 'gomulu veri gorseli', yonerge: 'img-src', adres: 'data:image/png;base64,iVBOR' },
  { ad: 'alt alan cagrisi', yonerge: 'connect-src', adres: 'https://olcum1.veri.ornek.test/kayit' },
  { ad: 'yazi tipi', yonerge: 'font-src', adres: 'https://olcum.ornek.test/f/a.woff2' },
];

for (const istek of istekler) {
  const sonuc = karar(POLITIKA, istek, KAYNAK);
  const satir = [
    (sonuc.izinli ? 'izin' : 'engel').padEnd(6),
    istek.ad.padEnd(20),
    (sonuc.yonerge ?? '-').padEnd(14),
    sonuc.eslesen ?? '',
  ].join(' ');
  console.log(satir.trimEnd());
}

console.log('\nrapor kaydi:');
const engellenen = istekler[2];
console.log(JSON.stringify(raporKaydi(engellenen, karar(POLITIKA, engellenen, KAYNAK), POLITIKA, 'enforce'), null, 2));
```

```
$ node csp.mjs
izin   kendi betigi         script-src     'self'
izin   dagitim betigi       script-src     https://olcum-cdn.ornek.test
engel  yabanci betik        script-src
izin   imli satir ici       script-src     'nonce-r4k2'
engel  imsiz satir ici      script-src
izin   gomulu veri gorseli  img-src        data:
izin   alt alan cagrisi     connect-src    https://*.veri.ornek.test
engel  yazi tipi            default-src

rapor kaydi:
{
  "document-uri": "https://olcum.ornek.test/istasyon/kuzey-yamac",
  "violated-directive": "script-src",
  "blocked-uri": "https://baska.ornek.test/izleme.js",
  "disposition": "enforce",
  "original-policy": "default-src 'none'; script-src 'self' 'nonce-r4k2' https://olcum-cdn.ornek.test; img-src 'self' data:; connect-src 'self' https://*.veri.ornek.test; frame-ancestors 'none'"
}
```

Son satır politikanın en öğretici davranışını gösterir. Yazı tipi isteği `font-src`
yönergesi bulunmadığı için `default-src 'none'` listesine düşer ve engellenir. Bu, sıkı
politika yazmanın önerilen biçimidir: `default-src 'none'` ile başlanır, uygulamanın
gerçekten kullandığı her tür için yönerge açıkça eklenir. Eksik yönerge sessizce izin
vermek yerine görünür bir hataya dönüşür.

## İm ve Karma ile Satır İçi Kod

Satır içi betik gerçekten gerekiyorsa iki yol vardır ve ikisi de `'unsafe-inline'`
kullanmaz.

**İm (nonce)**, sunucunun her yanıt için ürettiği tahmin edilemez bir değerdir. Politikada
`'nonce-r4k2'` biçiminde bildirilir, betik ögesinde aynı değer `nonce` özniteliği olarak
yazılır. Tarayıcı yalnız eşleşen ögeyi çalıştırır. Üç kural bağlayıcıdır: değer kriptografik
kalitede rastgele üretilir, her yanıtta yenilenir ve önbelleğe alınan bir sayfayla birlikte
yeniden kullanılmaz. Sabit bir im, politikayı `'unsafe-inline'` düzeyine indirir.

**Karma tabanlı izin**, içeriği değişmeyen satır içi betikler için uygundur. Betiğin
gövdesinin özeti alınır ve politikaya `'sha256-…'` biçiminde yazılır. Yanıt başına üretim
gerektirmez, ama betik metni bir karakter değiştiğinde politika da güncellenmelidir; bu da
karmanın derleme aşamasında hesaplanmasını gerektirir.

Konak listeleri büyüdükçe politikanın koruma değeri düşer, çünkü listedeki her alan adı
güvenilen bir yükleme noktası hâline gelir. Bu sorunu azaltmak için tanımlanmış
`'strict-dynamic'` ifadesi, imle çalıştırılmış bir betiğin yüklediği alt betiklere güveni
devreder ve konak listesini gereksizleştirir. Bu ifadeyi anlamayan bir gerçekleştirimde
politikanın çalışmaya devam etmesi için, ifade konak listesiyle birlikte yazılır: eski
davranış listeyi okur, yeni davranış listeyi yok sayar.

## Rapor Kipi ve Yayına Alma

Sıkı bir politikayı hazır bir uygulamaya doğrudan uygulamak, çalışan işlevleri kırar.
`Content-Security-Policy-Report-Only` başlığı bu yüzden vardır: tarayıcı politikayı
değerlendirir, ihlalleri bildirir, ama hiçbir şeyi engellemez. Yukarıdaki rapor kaydında
`disposition` alanı bu ayrımı taşır — zorlama kipinde `enforce`, rapor kipinde `report`
değerini alır.

İhlal kayıtları, politikada bildirilen bir toplama ucuna gönderilir. Kaydın alanları
ihlalin hangi belgede oluştuğunu, hangi yönergenin ihlal edildiğini ve neyin engellendiğini
söyler. Satır içi kod için `blocked-uri` alanı bir adres yerine `inline` değerini taşır.

Yayına alma sırası şudur: rapor kipiyle başlanır, gelen kayıtlar bir süre toplanır, meşru
yükleme noktaları politikaya eklenir, kalan ihlaller kod düzeltmesiyle giderilir; ancak
kayıt akışı durulduktan sonra zorlama kipine geçilir. Toplama ucunun kendisi de
gürültüye dayanıklı olmalıdır: kullanıcı tarafında sayfaya kod ekleyen eklentiler,
uygulamayla ilgisi olmayan ihlaller üretir ve bu kayıtlar ayıklanmadığında gerçek
bulgular görünmez olur.

## Politikanın Kapsamadığı Alan

Politika yükleme ve çalıştırma kararlarını denetler; belge içinde çalışan meşru kodun ne
yaptığını denetlemez. Önceki derste tanıtılan DOM tabanlı sınıf bu boşluğa düşer:
uygulamanın kendi betiği, kendi kaynağından gelen bir değeri ham işaretleme olarak yazarsa
politika bunu bir dış yükleme saymaz.

Bu boşluğu daraltmak için tanımlanmış olan mekanizma, ham işaretleme kabul eden yerleştirme
noktalarını yalnız denetlenmiş bir tür üzerinden yazılabilir kılar ve dizgi atamasını hata
hâline getirir. Politikaya bir yönergeyle bağlanır ve rapor kipiyle ölçülebilir. Desteği
her ortamda bulunmayabileceği için, yetenek saptamayla sınanır ve bulunmadığında uygulama
önceki derste kurulan kaçırma katmanına dayanır.

## Özet

- İçerik güvenlik politikası, hangi kaynağın yüklenip hangi kodun çalıştırılabileceğini
  tarayıcıya bildirir ve kararı uygulamanın kendi kodundan bağımsız kılar.
- Politika yönergelerden oluşur; `default-src` diğerlerinin yedeğidir, bu yüzden
  `default-src 'none'` ile başlayıp gerekli türleri tek tek eklemek eksikleri görünür kılar.
- Satır içi kod için `'unsafe-inline'` yerine yanıt başına yenilenen im veya derleme
  aşamasında hesaplanan karma kullanılır.
- Rapor kipi politikayı engellemeden değerlendirir; yayına alma rapor kipiyle başlar,
  kayıt akışı durulduktan sonra zorlama kipine geçilir.
- Politika dış yükleme kararlarını denetler; belgenin kendi kodunun ham işaretleme yazması
  ayrı bir mekanizmayla sınırlandırılır.

## Sonraki Adım

Politika, betiğin nereden yüklenebileceğini söyler. Betik yüklendikten sonra veri okumak
için başka bir kaynağa istek attığında ise başka bir kural kümesi devreye girer: aynı kaynak
politikası, bu isteğin yanıtının okunmasına öntanımlı olarak izin vermez. Ölçüm arayüzünün
ayrı bir alan adındaki veri ucundan okuma yapabilmesi için sunucunun açık bir izin
bildirmesi gerekir. Sonraki ders bu izin mekanizmasını, tarayıcının uyguladığı kısıtları ve
doğru sunucu yapılandırmasını kurar.
