---
title: 'Kaynaklar Arası Erişim Denetimi'
source: 'https://academia.sh/tr/kurslar/backend-uretim/kaynaklar-arasi-erisim-denetimi'
course: 'Sunucu Güvenliği ve Üretime Alma'
language: tr
updated: '2026-08-17T18:06:49+00:00'
license: 'CC BY-SA 4.0'
---

# Kaynaklar Arası Erişim Denetimi

Kaynaklar arası paylaşımın sunucu tarafındaki yapılandırması: yedi kökenin dört ayar altında kaç tanesinin yanıtı kullanabildiği, joker ayarın açtığı köken sayısı, kimlik bilgisi taşıyan istekte joker ayarın sunucuda 200 döndürüp istemcide sessizce atılması, ön denetim isteğinin gidiş-dönüş maliyeti ve politikanın kaç uçta kayıtlı olduğu.

Önceki iki ders isteğin gövdesine baktı. Bu ders isteğin **nereden geldiğine** bakıyor ve
sunucunun bu konuda söyleyebileceği tek şeyi ölçüyor: yanıtın başlıkları.

Kaynaklar arası paylaşımın istemci tarafı — tarayıcının hangi isteği neden engellediği,
politikanın sayfa üzerindeki etkisi — Frontend Kalitesi kursunda kuruldu ve burada
tekrarlanmaz. Bu derste ölçülen şey **sunucu tarafındaki yapılandırmadır**: ayar hangi
katmanda duruyor, joker bir değer kaç kökene kapı açıyor, kimlik bilgisi taşıyan bir istekte
yanlış ayar ne yapıyor ve ön denetim isteği kaça mal oluyor.

Sunucunun yaptığı iş küçüktür: gelen kökene bakar, birkaç yanıt başlığı ekler ya da eklemez.
Kararı **istemci** verir. Bu ayrım, dersin bütün ölçümlerinin dayandığı noktadır.

**BG5 (varsayım):** istemcinin kuralı bu derste kendi yazıldı. Sunucu ayarının doğruluğu,
o kurala göre yanıtın kullanılıp kullanılmadığıyla ölçülüyor; ölçüm gerçek bir istemcinin
değil, kuralın gerçekleştiriminin sonucudur.

## Sunucunun Tek Sözü

```js
// politika.mjs — sunucunun kaynaklar arasi paylasim konusundaki tek sozu: yanit basliklari
export const AYAR = {
  kokenler: ["https://odunc.kutuphane", "https://panel.kutuphane"],   // "*" joker demektir
  yontemler: ["GET", "POST"],
  basliklar: ["content-type", "x-odunc-izlem"],
  kimlikBilgisi: false,
  onDenetimOmru: 600,
};

export function basliklar(istek, ayar) {
  const c = {};
  if (!istek.koken) return c;                       // ayni kokenli istek; soz gerekmez
  const joker = ayar.kokenler === "*";
  if (!joker && !ayar.kokenler.includes(istek.koken)) return c;   // suskunluk = ret
  c["access-control-allow-origin"] = joker ? "*" : istek.koken;
  if (ayar.kimlikBilgisi) c["access-control-allow-credentials"] = "true";
  if (!joker) c["vary"] = "origin";                 // yanit kokene gore degisiyor
  if (istek.yontem === "OPTIONS") {
    c["access-control-allow-methods"] = ayar.yontemler.join(",");
    c["access-control-allow-headers"] = ayar.basliklar.join(",");
    c["access-control-max-age"] = String(ayar.onDenetimOmru);
  }
  return c;
}
```

```js
// istemci.mjs — uyumlu bir istemcinin yanit basliklarina bakarak verdigi karar
// Kural burada kendi yazildi; sunucu ayarinin dogrulugu bu karara gore olculuyor.
export function karar(istek, b) {
  if (!istek.koken) return { kullanildi: true, neden: "ayni koken" };
  const izin = b["access-control-allow-origin"];
  if (!izin) return { kullanildi: false, neden: "izin basligi yok" };
  if (izin !== "*" && izin !== istek.koken) return { kullanildi: false, neden: "koken uyusmuyor" };
  if (istek.kimlikBilgisi) {
    if (izin === "*") return { kullanildi: false, neden: "joker + kimlik bilgisi" };
    if (b["access-control-allow-credentials"] !== "true")
      return { kullanildi: false, neden: "kimlik bilgisi izni yok" };
  }
  return { kullanildi: true, neden: "" };
}
```

Politika katmanının reddi bir hata değil, **suskunluktur**: tanımadığı bir kökene hiçbir
başlık eklemez. Sunucu yine 200 döner, gövde yine yazılır. Bu tasarım kararı dersin geri
kalanındaki bütün sessiz sonuçların kaynağıdır.

## Joker Ayarın Açtığı Köken

Yedi köken alınıyor: ödünç sisteminin kendi arayüzü, iç panel, aynı adın şema ve kapı
farkları, bir alt ad, ilgisiz bir köken ve köken başlığı hiç taşımayan aynı kökenli istek.

```js
// koken.mjs — yedi koken, dort ayar; sunucunun dondugu ile istemcinin kullandigi
import { AYAR, basliklar } from "./politika.mjs";
import { karar } from "./istemci.mjs";

const KOKENLER = [
  null,                                // ayni koken (baslik yok)
  "https://odunc.kutuphane",           // kendi arayuzu
  "https://panel.kutuphane",           // ic panel
  "http://odunc.kutuphane",            // sema farkli
  "https://odunc.kutuphane:8443",      // kapi farkli
  "https://alt.odunc.kutuphane",       // alt ad
  "https://baska.site",                // ilgisiz
];

const AYARLAR = [
  ["liste, kimliksiz", { ...AYAR }],
  ["joker, kimliksiz", { ...AYAR, kokenler: "*" }],
  ["joker, kimlikli", { ...AYAR, kokenler: "*", kimlikBilgisi: true }],
  ["liste, kimlikli", { ...AYAR, kimlikBilgisi: true }],
];

const gec = (ayar, koken, kimlikBilgisi) => {
  const istek = { koken, yontem: "GET", kimlikBilgisi };
  return karar(istek, basliklar(istek, ayar)).kullanildi;
};

console.log(`${KOKENLER.length} koken (1 ayni koken + ${KOKENLER.length - 1} dis koken), ${AYARLAR.length} ayar`);
console.log(`\n${"ayar".padEnd(19)}${"sunucu 2xx".padStart(11)}${"istemci kullandi: kimliksiz".padStart(28)}${"kimlikli".padStart(10)}`);
for (const [ad, ayar] of AYARLAR) {
  const k0 = KOKENLER.filter((k) => gec(ayar, k, false)).length;
  const k1 = KOKENLER.filter((k) => gec(ayar, k, true)).length;
  console.log(ad.padEnd(19) + String(KOKENLER.length).padStart(11) + String(k0).padStart(28) + String(k1).padStart(10));
}

const jokerAcik = KOKENLER.filter((k) => k && gec({ ...AYAR, kokenler: "*" }, k, false));
const listeAcik = KOKENLER.filter((k) => k && gec(AYAR, k, false));
console.log(`\nliste ayarinin actigi dis koken: ${listeAcik.length} (${listeAcik.join(", ")})`);
console.log(`joker ayarinin bu kumede actigi dis koken: ${jokerAcik.length}; kume disinda sinir yok`);

console.log(`\n'joker, kimlikli' ayarinda kimlik bilgisi tasiyan istekler:`);
for (const k of KOKENLER.slice(1, 4)) {
  const istek = { koken: k, yontem: "GET", kimlikBilgisi: true };
  const b = basliklar(istek, { ...AYAR, kokenler: "*", kimlikBilgisi: true });
  const c = karar(istek, b);
  console.log(`  ${k.padEnd(30)} sunucu 200 + ${Object.keys(b).length} baslik -> istemci: ${c.kullanildi ? "kullandi" : "atti (" + c.neden + ")"}`);
}
```

```
7 koken (1 ayni koken + 6 dis koken), 4 ayar

ayar                sunucu 2xx istemci kullandi: kimliksiz  kimlikli
liste, kimliksiz             7                           3         1
joker, kimliksiz             7                           7         1
joker, kimlikli              7                           7         1
liste, kimlikli              7                           3         3

liste ayarinin actigi dis koken: 2 (https://odunc.kutuphane, https://panel.kutuphane)
joker ayarinin bu kumede actigi dis koken: 6; kume disinda sinir yok

'joker, kimlikli' ayarinda kimlik bilgisi tasiyan istekler:
  https://odunc.kutuphane        sunucu 200 + 2 baslik -> istemci: atti (joker + kimlik bilgisi)
  https://panel.kutuphane        sunucu 200 + 2 baslik -> istemci: atti (joker + kimlik bilgisi)
  http://odunc.kutuphane         sunucu 200 + 2 baslik -> istemci: atti (joker + kimlik bilgisi)
```

`sunucu 2xx` sütunu dört ayarda da yedidir. Sunucu hiçbir isteği reddetmiyor; ayar yalnız
istemcinin yanıtı kullanıp kullanmayacağını belirliyor. Liste ayarı iki dış kökene açıktır;
joker ayarı bu kümedeki altısına ve **kümenin dışında sınır yoktur** — şema farkı, kapı
farkı ve alt ad dahil, adı hiç yazılmamış her köken içeri girer. Joker ile listenin farkı
bu satırda 2'ye karşı 6'dır; gerçekte 2'ye karşı sınırsızdır.

Üçüncü satır bu dersin sessiz ayarıdır. `kimlikBilgisi` açılmış, sunucu iki başlık birden
gönderiyor, istek 200 dönüyor — ve istemci yanıtı **atıyor**. `joker, kimlikli` satırı
`joker, kimliksiz` satırıyla birebir aynı: ayarı açmak hiçbir şeyi değiştirmedi. Bir kayıt
yığınında bu iki ayar ayırt edilemez; iki durumda da 200 vardır, hata yoktur, gecikme
yoktur. Yalnız iç panel çalışmaz ve nedeni sunucu tarafında görünmez.

Doğru ayar son satırdır: köken listesi **ve** kimlik bilgisi izni birlikte. Üç kimlikli
istek ancak orada kullanılıyor.

## Ön Denetimin Maliyeti

Politika katmanı gerçek bir süreçte ölçülüyor. Özel bir başlık taşıyan istek, gönderilmeden
önce bir **ön denetim** isteği gerektirir; ön denetimin sonucu bir ömür boyunca saklanabilir.

```js
// sunucu.mjs — odunc uclarini dinleyen yerel surec; politika katmani uclarin onunde durur
import http from "node:http";
import { basliklar } from "./politika.mjs";

export const UCLAR = ["/katalog", "/odunc", "/uye", "/rapor", "/arsiv"];

export function baslat(ayar, kapsanan = UCLAR) {
  const sayac = { istek: 0, onDenetim: 0, bayt: 0 };
  const s = http.createServer((q, y) => {
    sayac.istek++;
    if (q.method === "OPTIONS") sayac.onDenetim++;
    const istek = { koken: q.headers.origin ?? null, yontem: q.method };
    const b = kapsanan.includes(q.url) ? basliklar(istek, ayar) : {};
    const govde = q.method === "OPTIONS" ? "" : JSON.stringify({ uc: q.url, durum: "ok" });
    sayac.bayt += govde.length +
      Object.entries(b).reduce((t, [k, v]) => t + k.length + v.length + 4, 0);
    y.writeHead(q.method === "OPTIONS" ? 204 : 200, { "content-type": "application/json", ...b });
    y.end(govde);
  });
  return new Promise((c) => s.listen(0, "127.0.0.1", () =>
    c({ port: s.address().port, sayac, kapat: () => s.close() })));
}
```

```js
// olcum.mjs — gercek istekler: on denetim maliyeti ve katman kapsaminin olcusu
import { baslat, UCLAR } from "./sunucu.mjs";
import { AYAR } from "./politika.mjs";
import { karar } from "./istemci.mjs";

const KOKEN = "https://panel.kutuphane";
const N = 6;

// Uyumlu istemci: ozel baslikli istek once on denetim ister, sonuc omru boyunca onbelleklenir.
async function gonder(port, uc, { ozel, onbellek, kimlikBilgisi = false }) {
  const kok = `http://127.0.0.1:${port}`;
  if (ozel && !(onbellek && onbellek.has(uc))) {
    const o = await fetch(kok + uc, { method: "OPTIONS", headers: { origin: KOKEN } });
    if (onbellek) onbellek.set(uc, Number(o.headers.get("access-control-max-age") ?? 0));
  }
  const basliklar = { origin: KOKEN, ...(ozel ? { "x-odunc-izlem": "1" } : {}) };
  const y = await fetch(kok + uc, { headers: basliklar });
  const b = Object.fromEntries(y.headers);
  return { durum: y.status, karar: karar({ koken: KOKEN, kimlikBilgisi }, b) };
}

const { port, sayac, kapat } = await baslat(AYAR);

const kosum = async (ad, secenek) => {
  const once = { ...sayac };
  for (let i = 0; i < N; i++) await gonder(port, "/katalog", secenek);
  console.log(`${ad.padEnd(28)}${String(sayac.istek - once.istek).padStart(8)}` +
    `${String(sayac.onDenetim - once.onDenetim).padStart(12)}${String(sayac.bayt - once.bayt).padStart(9)}`);
};

console.log(`${N} gercek istek, tek uc\n`);
console.log(`${"istek bicimi".padEnd(28)}${"gidis-donus".padStart(8)}${"on denetim".padStart(12)}${"bayt".padStart(9)}`);
await kosum("basit (on denetim gerekmez)", { ozel: false });
await kosum("ozel baslikli, onbelleksiz", { ozel: true });
await kosum("ozel baslikli, onbellekli", { ozel: true, onbellek: new Map() });
console.log(`on denetim omru: ${AYAR.onDenetimOmru} sn -> omur boyunca uc basina 1 on denetim`);
kapat();

// Katman kapsami: politika bes ucun dordune kaydedilmis.
const EKSIK = UCLAR.filter((u) => u !== "/arsiv");
const iki = await baslat({ ...AYAR, kimlikBilgisi: true }, EKSIK);
console.log(`\npolitika kaydi ${EKSIK.length}/${UCLAR.length} ucta\n`);
console.log(`${"uc".padEnd(10)}${"sunucu durumu".padStart(14)}${"istemci".padStart(9)}  neden`);
let atilan = 0;
for (const uc of UCLAR) {
  const r = await gonder(iki.port, uc, { ozel: false, kimlikBilgisi: true });
  if (!r.karar.kullanildi) atilan++;
  console.log(uc.padEnd(10) + String(r.durum).padStart(14) +
    (r.karar.kullanildi ? "kullandi" : "atti").padStart(9) + "  " + (r.karar.neden || "-"));
}
console.log(`\n${UCLAR.length} ucun ${atilan} tanesinde yanit istemcide atildi; sunucunun dondugu hata sayisi: 0`);
iki.kapat();
```

```
6 gercek istek, tek uc

istek bicimi                gidis-donus  on denetim     bayt
basit (on denetim gerekmez)        6           0      588
ozel baslikli, onbelleksiz        12           6     1758
ozel baslikli, onbellekli          7           1      783
on denetim omru: 600 sn -> omur boyunca uc basina 1 on denetim

politika kaydi 4/5 ucta

uc         sunucu durumu  istemci  neden
/katalog             200 kullandi  -
/odunc               200 kullandi  -
/uye                 200 kullandi  -
/rapor               200 kullandi  -
/arsiv               200     atti  izin basligi yok

5 ucun 1 tanesinde yanit istemcide atildi; sunucunun dondugu hata sayisi: 0
```

Ön denetimin maliyeti koşumdan bağımsız bir sayıdır: önbelleksiz altı özel istek **on iki**
gidiş-dönüş yapıyor, yani iki katı. Ömür ayarı devreye girdiğinde sayı yediye düşüyor —
uç başına bir ön denetim. Politika baytı da üçe katlanıyor: 588'e karşı 1758.

Bu maliyet yalnız özel başlıklı isteklere aittir. İlk satır bunu gösteriyor: özel başlık
kullanmayan altı istek hiç ön denetim üretmiyor. **Ön denetimden kaçınmanın yolu ayarı
gevşetmek değil, isteğin biçimini sadeleştirmektir.**

## Ayarın Yeri

Son tablo katman sorusudur. Politika beş ucun dördünde kayıtlı; `/arsiv` ucu unutulmuş.
O uç istekleri **kabul ediyor**, gövdeyi yazıyor, 200 dönüyor. Yanıt istemcide atılıyor ve
sunucu tarafında bunun hiçbir izi yok: hata sayısı sıfır, durum kodu diğer dört uçla aynı.

Ayarın kaç yerde tekrarlandığı doğrudan bu riski belirliyor. Politika her uç işleyicisine
ayrı kaydediliyorsa beş kayıt noktası vardır ve altıncı uç eklendiğinde altıncı kayıt elle
yazılmalıdır. Politika uçların önünde tek bir katman olarak duruyorsa kayıt noktası birdir
ve yeni uç kendiliğinden kapsanır — `sunucu.mjs` içindeki `kapsanan` listesi tam da bu iki
seçenek arasındaki farktır.

## Özet

- Sunucunun kaynaklar arası paylaşımdaki tek sözü yanıt başlıklarıdır; reddi bir hata değil
  suskunluktur. Dört ayarın dördünde de sunucu yedi isteğin yedisine 2xx döndü.
- Köken listesi 2 dış kökene açıktır; joker ayar ölçülen kümedeki 6 kökene ve küme dışında
  sınırsız sayıda kökene açıktır — şema farkı, kapı farkı ve alt ad dahil.
- Joker ayara kimlik bilgisi izni eklemek hiçbir şeyi değiştirmedi: `joker, kimlikli` satırı
  `joker, kimliksiz` ile birebir aynı çıktı. Sunucu iki başlık gönderdi, 200 döndü, istemci
  yanıtı attı. Doğru ayar köken listesi ile kimlik bilgisi iznini birlikte kullanır.
- Ön denetimin maliyeti koşumdan bağımsızdır: önbelleksiz 6 özel istek 12 gidiş-dönüş ve
  1758 bayt, ömür ayarıyla 7 gidiş-dönüş ve 783 bayt, özel başlıksız 6 istek 0 ön denetim.
- Politika 5 ucun 4'üne kaydedilince atlanan uçtan gelen yanıt istemcide atıldı ve sunucuda
  0 hata göründü. Uç başına kayıt 5 noktada tekrarlanır; uçların önündeki tek katman
  1 noktada durur ve yeni ucu kendiliğinden kapsar.

## Sonraki Adım

Kaynaklar arası paylaşım ayarı bir yanıt başlığı kümesinden ibaretti ve ölçüm gösterdi ki
asıl soru başlığın **hangi katmanda** üretildiğidir. Aynı soru, yanıtın taşıdığı öteki
başlıklar için de geçerlidir: bağlantının nasıl kurulacağını, içeriğin nasıl
yorumlanacağını ve tarayıcının neyi yükleyebileceğini söyleyen başlıklar da aynı biçimde
uç uç dağılabilir ya da tek yerden konabilir. Sonraki ders bu başlıkları sayar: kaç uçta
tekrarlanıyorlar, tek katmana taşındığında kaç dosyaya dokunuluyor, eksik bir başlık hangi
davranışı sessizce açık bırakıyor ve şifreli bağlantının sonlandığı nokta bu resmin
neresinde duruyor.
