İçeriğe geç
academia.sh

Ders 16 / 27

Kaynaklar Arası Paylaşım

Aynı kaynak politikasının neyi engellediği, basit istek ile ön denetim ayrımı, paylaşım başlıklarının anlamı, kimlik bilgili isteklerin kuralları ve sık görülen yanlış yapılandırmalar.

İçindekiler

Önceki ders, içerik güvenlik politikasının hangi kaynaktan betik yüklenebileceğini belirlediğini kurdu. Betik yüklendikten sonra ayrı bir sınır devreye girer. Kuzey Yamaç Ölçüm İstasyonu arayüzü https://olcum.ornek.test adresinden sunuluyor, ölçüm verisi ise ayrı bir alan adındaki bir uçtan geliyor. Arayüzün betiği bu uca istek attığında yanıtı okuyup okuyamayacağına politika değil, aynı kaynak politikası karar verir.

Bu ders o kararı ve kararı gevşetmenin denetimli yolunu kurar.

Aynı Kaynak Politikası Neyi Engeller

Tarayıcı ve Web Platformu kursunda tanımlandığı gibi bir kaynak (origin) şema, ana bilgisayar adı ve bağlantı noktasının oluşturduğu üçlüdür. Üçlünün herhangi bir bileşeni farklıysa kaynak farklıdır: https://olcum.ornek.test ile https://veri.ornek.test ayrı kaynaklardır, http://olcum.ornek.test de ayrıdır.

Aynı kaynak politikasının ne yaptığı konusundaki en sık yanılgı, isteğin gönderilmesini engellediği sanısıdır. Engellenen şey yanıtın okunmasıdır. Betik başka bir kaynağa istek gönderebilir, istek sunucuya ulaşır, sunucu isteği işler ve yanıt verir; tarayıcı yanıtı betiğe teslim etmez. İstek yan etki üretmişse o yan etki gerçekleşmiştir.

Bu ayrım sonraki dersin konusunu doğurur ve burada not edilmelidir: politika okumayı kısıtlar, yazmayı değil.

Politika yalnız betikten açılan bağlantılar için geçerlidir. Görsel, biçem dosyası, yazı tipi ve betik ögeleri başka kaynaklardan yükleme yapabilir; belge bunları gömer ama içeriklerini okuyamaz. Tarayıcı ve Web Platformu kursundaki Servis Çalışanları dersinde tanıtılan saydam yanıt kavramı bu durumun adıdır: yanıt kullanılabilir, ama gövdesi ve başlıkları program tarafından görülemez.

Basit İstek ve Ön Denetim

Kaynaklar arası isteklerin bir bölümü doğrudan gönderilir, bir bölümü gönderilmeden önce izin sorar.

Doğrudan gönderilen istekler, formların uzun süredir kaynaklar arası yapabildiği isteklerdir: GET, HEAD veya POST yöntemi, sınırlı bir içerik türü kümesi ve uygulamanın eklemediği başlıklar. Bu istekler için ek bir tur yoktur; tarayıcı isteği gönderir ve yanıtın okunup okunamayacağına dönen başlıklara bakarak karar verir.

Bunun dışındaki her istek ön denetim (preflight) gerektirir. Yöntem PUT veya DELETE ise, içerik türü application/json ise, uygulama X- ile başlayan bir başlık ekliyorsa tarayıcı önce OPTIONS yöntemiyle ayrı bir istek gönderir. Bu istekte Access-Control-Request-Method ve Access-Control-Request-Headers başlıkları, asıl isteğin ne yapacağını sunucuya bildirir. Sunucu izin verirse asıl istek gönderilir; vermezse asıl istek hiç oluşmaz.

Ön denetimin varlık nedeni geriye dönük uyumluluktur. Tarayıcının doğrudan gönderebileceği istekler zaten formlarla gönderilebilen isteklerdir; yeni yetenekler sunucunun haberi olmadan kullanıma açılmaz.

Sunucu Tarafındaki Yapılandırma

Aşağıdaki sunucu ölçüm verisini sunar ve paylaşım kararını tek bir izin listesi üzerinden verir.

// cors-sunucu.mjs — olcum ucunun kaynaklar arasi paylasim yapilandirmasi
import { createServer } from 'node:http';

const IZINLI_KAYNAKLAR = new Set(['https://olcum.ornek.test']);
const IZINLI_YONTEMLER = 'GET, POST, OPTIONS';
const IZINLI_BASLIKLAR = 'content-type, x-olcum-anahtari';

const sunucu = createServer((istek, yanit) => {
  const kaynak = istek.headers.origin;
  const izinli = kaynak !== undefined && IZINLI_KAYNAKLAR.has(kaynak);

  yanit.setHeader('Vary', 'Origin');
  if (izinli) {
    yanit.setHeader('Access-Control-Allow-Origin', kaynak);
    yanit.setHeader('Access-Control-Allow-Credentials', 'true');
  }

  if (istek.method === 'OPTIONS') {
    if (izinli) {
      yanit.setHeader('Access-Control-Allow-Methods', IZINLI_YONTEMLER);
      yanit.setHeader('Access-Control-Allow-Headers', IZINLI_BASLIKLAR);
      yanit.setHeader('Access-Control-Max-Age', '600');
    }
    yanit.writeHead(204).end();
    return;
  }

  yanit.setHeader('Content-Type', 'application/json');
  yanit.end(JSON.stringify({ istasyon: 'kuzey-yamac', sicaklik: -3.4 }));
});

sunucu.listen(8791, '127.0.0.1', () => {
  console.log('dinliyor: http://127.0.0.1:8791');
});

Sunucu arka planda başlatılır, üç istek gönderilir ve sonunda durdurulur. Bayrak -D - yanıt başlıklarını standart çıktıya yazar; tr -d '\r' satır sonlarındaki taşıma karakterini ayıklar, grep ise her çalıştırmada değişecek olan tarih satırını eler.

node cors-sunucu.mjs &
sunucu=$!
sleep 1

echo '--- 1. izinli kaynaktan on denetim ---'
curl -s -o /dev/null -D - -X OPTIONS http://127.0.0.1:8791/olcum \
  -H 'Origin: https://olcum.ornek.test' \
  -H 'Access-Control-Request-Method: POST' \
  -H 'Access-Control-Request-Headers: content-type' | tr -d '\r' | grep -v '^Date:'

echo '--- 2. listede olmayan kaynaktan on denetim ---'
curl -s -o /dev/null -D - -X OPTIONS http://127.0.0.1:8791/olcum \
  -H 'Origin: https://baska.ornek.test' \
  -H 'Access-Control-Request-Method: POST' \
  -H 'Access-Control-Request-Headers: content-type' | tr -d '\r' | grep -v '^Date:'

echo '--- 3. izinli kaynaktan gercek istek ---'
curl -s -D - http://127.0.0.1:8791/olcum \
  -H 'Origin: https://olcum.ornek.test' | tr -d '\r' | grep -v '^Date:'

kill $sunucu
dinliyor: http://127.0.0.1:8791
--- 1. izinli kaynaktan on denetim ---
HTTP/1.1 204 No Content
Vary: Origin
Access-Control-Allow-Origin: https://olcum.ornek.test
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: content-type, x-olcum-anahtari
Access-Control-Max-Age: 600
Connection: keep-alive
Keep-Alive: timeout=5

--- 2. listede olmayan kaynaktan on denetim ---
HTTP/1.1 204 No Content
Vary: Origin
Connection: keep-alive
Keep-Alive: timeout=5

--- 3. izinli kaynaktan gercek istek ---
HTTP/1.1 200 OK
Vary: Origin
Access-Control-Allow-Origin: https://olcum.ornek.test
Access-Control-Allow-Credentials: true
Content-Type: application/json
Connection: keep-alive
Keep-Alive: timeout=5
Content-Length: 42

{"istasyon":"kuzey-yamac","sicaklik":-3.4}

İkinci durum, reddin nasıl göründüğünü gösterir. Sunucu bir hata kodu döndürmez; yalnızca izin başlıklarını yazmaz. Karar tarayıcıya aittir ve tarayıcı, izin başlığı bulunmayan bir ön denetimden sonra asıl isteği hiç göndermez. Bu, ret durumunun neden sunucu günlüğünde bir hata olarak görünmediğini de açıklar.

Başlıkların Anlamı

Access-Control-Allow-Origin, yanıtı okumasına izin verilen tek kaynağı bildirir. Değeri ya tam bir kaynak dizgisidir ya da tüm kaynakları gösteren yıldızdır.

Access-Control-Allow-Methods ve Access-Control-Allow-Headers yalnız ön denetim yanıtında anlamlıdır; asıl isteğin hangi yöntemi ve hangi başlıkları kullanabileceğini sayar. Access-Control-Max-Age, ön denetim sonucunun kaç saniye önbelleğe alınabileceğini söyler; her istekten önce bir tur daha atılmasını engeller.

Access-Control-Expose-Headers, betiğin yanıt üzerinde hangi başlıkları okuyabileceğini genişletir. Öntanımlı olarak yalnızca küçük bir başlık kümesi okunabilir; sayfalama bilgisini özel bir başlıkta taşıyan bir uç, o başlığı burada saymazsa betik başlığı göremez.

Vary: Origin bir izin başlığı değildir ama eksikliği gerçek bir kusurdur. Yanıtın içeriği istekteki Origin başlığına göre değiştiği için, aradaki her önbellek bu başlığı önbellek anahtarına katmalıdır. Katmazsa, izinli bir kaynak için üretilmiş izin başlıkları başka bir kaynağa sunulabilir.

Kimlik Bilgili İstekler

Çerezlerin ve kimlik başlıklarının kaynaklar arası isteğe eklenmesi ayrıca istenmelidir. İstemci tarafında bu, isteğin kimlik bilgisi taşıyacağının belirtilmesiyle olur; sunucu tarafında iki kural birden geçerlidir.

Birincisi, Access-Control-Allow-Credentials başlığı true değerini taşımalıdır. İkincisi, Access-Control-Allow-Origin yıldız olamaz; tam kaynak dizgisi yazılmalıdır. Bu kural, “herkese açık” bir yapılandırmanın yanlışlıkla kimlik bilgisi taşıyan istekleri kapsamasını engeller.

Çerezin gerçekten gönderilmesi ayrı bir koşula daha bağlıdır: çerezin SameSite niteliği kaynaklar arası gönderime izin vermelidir. Bu nitelik sonraki dersin konusudur ve iki mekanizma birlikte değerlendirilmelidir; paylaşım başlıkları doğru olsa bile çerez niteliği engelliyorsa istek kimliksiz gider.

Sık Görülen Yanlış Yapılandırmalar

Gelen kaynağı olduğu gibi yansıtmak. İstekteki Origin başlığını doğrulamadan yanıta yazan bir sunucu, izin listesi tutuyor gibi görünür ama her kaynağa izin vermiş olur. Yukarıdaki örnekte yansıtma yapılıyor, ancak yalnız listede bulunan bir değer için.

Yıldızı kimlik bilgisiyle birlikte kullanmaya çalışmak. Tarayıcı bunu reddeder; bu reddi aşmak için kaynağı yansıtmaya geçmek, ilk kusuru üretir.

Alan adı eşleşmesini gevşek yazmak. ornek.test ile biten her adı kabul eden bir denetim, ornek.test.baska.test adını da kabul eder. Eşleşme dizgi sonu üzerinden değil, tam kaynak karşılaştırmasıyla yapılır.

null kaynağına izin vermek. Bazı bağlamlar Origin başlığını null olarak gönderir; bu değeri izin listesine koymak, o bağlamların hepsine izin vermek demektir.

Paylaşım Bir Yetkilendirme Değildir

Kaynaklar arası paylaşım, kullanıcının tarayıcısını başka bir sitenin betiğinin veri okumasına karşı korur. Sunucuyu korumaz. Tarayıcı olmayan bir istemci bu başlıkları hiç dikkate almaz; izin listesinde bulunmayan bir kaynak adı yazmak da bir engel oluşturmaz, çünkü Origin başlığını tarayıcı dışında hiçbir şey zorunlu kılmaz.

Bundan çıkan kural nettir: bir uç, kimlik doğrulaması ve yetkilendirmesi olmadan korunmuş sayılmaz. Paylaşım yapılandırması bu denetimlerin yerine geçmez, üzerine eklenir.

Özet

  • Aynı kaynak politikası kaynaklar arası isteğin gönderilmesini değil, yanıtının okunmasını engeller; yan etki üreten bir istek yine de gerçekleşir.
  • Basit koşulları sağlamayan istekler OPTIONS yöntemiyle bir ön denetim turu geçirir; sunucu izin vermezse asıl istek hiç oluşmaz.
  • Ret, hata kodu olarak değil, izin başlıklarının yokluğu olarak görünür.
  • Kimlik bilgili isteklerde Access-Control-Allow-Origin yıldız olamaz ve Access-Control-Allow-Credentials gerekir; çerezin SameSite niteliği ayrıca izin vermelidir.
  • Paylaşım yapılandırması tarayıcı içindir; sunucunun kimlik doğrulaması ve yetkilendirmesi bundan bağımsız olarak gereklidir.

Sonraki Adım

Bu dersin başındaki ayrım açık bir soru bıraktı: aynı kaynak politikası okumayı engelliyorsa ve yazma isteği yine de sunucuya ulaşıyorsa, başka bir sitenin sayfası kullanıcının oturumu üzerinden bir ölçüm kaydı silebilir mi? Yanıtın okunamaması, işlemin yapılmadığı anlamına gelmez. Sonraki ders bu saldırı sınıfını, çerez niteliklerinin ve istek belirteçlerinin oluşturduğu savunmayı ele alır.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat