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
OPTIONSyö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-Originyıldız olamaz veAccess-Control-Allow-Credentialsgerekir; çerezinSameSiteniteliğ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.