Ders 17 / 27
İstek Sahteciliği
Çerezlerin kendiliğinden eklenmesinden doğan saldırı sınıfı; SameSite ve Secure niteliklerinin karar tablosu, oturuma bağlanmış istek belirteci, kaynak başlığı denetimi ve yöntem disiplini.
İçindekiler
Önceki ders bir soruyla bitti: aynı kaynak politikası yanıtın okunmasını engelliyorsa, yazma isteği ne oluyor? Yanıt, engellenmediğidir. Kaynaklar arası bir istek sunucuya ulaşır, sunucu isteği işler, yan etki gerçekleşir; yalnız yanıt gövdesi isteği başlatan sayfaya teslim edilmez.
Bu boşluk tek başına sorun değildir. Sorun, isteğe kullanıcının kimliğinin kendiliğinden eklenmesiyle oluşur. Kuzey Yamaç Ölçüm İstasyonu arayüzünde oturum bir çerezle taşınıyorsa, tarayıcı o çerezi isteğin nereden başlatıldığına bakmadan ekler. Bu ders o davranışın sonucunu ve iki bağımsız savunma katmanını kurar.
Ortam Yetkisi
Çerez, İnternet Nasıl Çalışır kursunda tanımlandığı gibi istekle birlikte kendiliğinden gider. Bu, oturumun sürmesini sağlayan davranıştır: kullanıcı her sayfada kimliğini yeniden bildirmez. Aynı davranış bir yan sonuç üretir. İsteği kimin başlattığı çerezin eklenmesini etkilemez; tarayıcı hedef adrese bakar, kaynağa değil.
Buna ortam yetkisi (ambient authority) denir: yetki, isteği yapan koda değil, isteğin gittiği yere iliştirilmiştir. Siteler arası istek sahteciliği (cross-site request forgery) bu yetkinin başka bir sitenin sayfasından tetiklenmesidir. Kullanıcı ölçüm istasyonuna giriş yapmış durumdayken başka bir sekmede bir sayfa açar; o sayfa, ölçüm istasyonunun bir uç adresine yazma isteği başlatır. İstek kullanıcının çerezini taşıdığı için sunucu tarafında meşru görünür.
Saldırıya betik gerekmez. Bir form ögesi gönderilebilir, bir görsel ögesi bir adresi çağırabilir. Yanıtın okunamaması saldırganın umurunda değildir; amaç okumak değil, yaptırmaktır.
Birinci Katman: Çerez Nitelikleri
İlk savunma çerezin kendisindedir. SameSite niteliği, çerezin kaynaklar arası isteklere
eklenip eklenmeyeceğini belirler ve üç değer alır. Aşağıdaki kod bu değerlerin karar
tablosunu hesaplar.
// samesite.mjs — cerez nitelikleri istek turlerine gore karar tablosu uretir const GUVENLI_YONTEMLER = new Set(['GET', 'HEAD']); function gonderilirMi(cerez, istek) { if (cerez.secure && istek.sema !== 'https') return ['hayir', 'Secure nitelikli cerez sifresiz semada gonderilmez']; if (istek.ayniSite) return ['evet', 'ayni site istegi']; switch (cerez.sameSite) { case 'Strict': return ['hayir', 'Strict kaynaklar arasi her istegi diser']; case 'Lax': return istek.ustDuzeyGezinme && GUVENLI_YONTEMLER.has(istek.yontem) ? ['evet', 'Lax ust duzey gezinmede guvenli yontemi gecirir'] : ['hayir', 'Lax yalniz ust duzey guvenli gezinmeyi gecirir']; case 'None': return cerez.secure ? ['evet', 'None kaynaklar arasi gonderir'] : ['hayir', 'None icin Secure zorunlu']; default: return ['hayir', 'taninmayan deger']; } } const istekler = [ { ad: 'ayni site cagri', ayniSite: true, ustDuzeyGezinme: false, yontem: 'GET', sema: 'https' }, { ad: 'disaridan bag (GET)', ayniSite: false, ustDuzeyGezinme: true, yontem: 'GET', sema: 'https' }, { ad: 'disaridan form (POST)', ayniSite: false, ustDuzeyGezinme: true, yontem: 'POST', sema: 'https' }, { ad: 'disaridan cerceve', ayniSite: false, ustDuzeyGezinme: false, yontem: 'GET', sema: 'https' }, { ad: 'disaridan betik cagrisi', ayniSite: false, ustDuzeyGezinme: false, yontem: 'POST', sema: 'https' }, ]; const cerezler = [ { ad: 'Strict+Secure', sameSite: 'Strict', secure: true }, { ad: 'Lax+Secure', sameSite: 'Lax', secure: true }, { ad: 'None+Secure', sameSite: 'None', secure: true }, { ad: 'None, Secure yok', sameSite: 'None', secure: false }, ]; const basliklar = ['istek turu'.padEnd(24), ...cerezler.map((c) => c.ad.padEnd(16))].join('').trimEnd(); console.log(basliklar); for (const istek of istekler) { const hucreler = cerezler.map((cerez) => gonderilirMi(cerez, istek)[0].padEnd(16)); console.log(istek.ad.padEnd(24) + hucreler.join('').trimEnd()); } console.log('\ngerekce ornegi (Lax+Secure):'); for (const istek of istekler) { const [karar, neden] = gonderilirMi(cerezler[1], istek); console.log(' ', istek.ad.padEnd(24), karar.padEnd(6), neden); }
$ node samesite.mjs istek turu Strict+Secure Lax+Secure None+Secure None, Secure yok ayni site cagri evet evet evet evet disaridan bag (GET) hayir evet evet hayir disaridan form (POST) hayir hayir evet hayir disaridan cerceve hayir hayir evet hayir disaridan betik cagrisi hayir hayir evet hayir gerekce ornegi (Lax+Secure): ayni site cagri evet ayni site istegi disaridan bag (GET) evet Lax ust duzey gezinmede guvenli yontemi gecirir disaridan form (POST) hayir Lax yalniz ust duzey guvenli gezinmeyi gecirir disaridan cerceve hayir Lax yalniz ust duzey guvenli gezinmeyi gecirir disaridan betik cagrisi hayir Lax yalniz ust duzey guvenli gezinmeyi gecirir
Tablo üç kuralı görünür kılar. Strict, kaynaklar arası her isteği diser; koruması en
geniştir ama başka bir siteden gelen bir bağla giren kullanıcı oturumsuz karşılanır.
Lax, yalnız üst düzey gezinmede ve yalnız güvenli yöntemle çerezi geçirir; dışarıdan
gelen bir formun POST isteği ile bir çerçeve yüklemesi eler. None her durumda gönderir
ve bu yüzden Secure niteliğini zorunlu kılar; nitelik yoksa çerez hiç gönderilmez.
Bu tabloyla birlikte iki nitelik daha oturum çerezinin ayrılmaz parçasıdır. HttpOnly,
çerezin betikten okunmasını engeller; ilk derste kurulan betik çalıştırma savunması
aşıldığında oturumun doğrudan çalınmasını zorlaştırır. Secure, çerezin şifresiz bir
bağlantıda gönderilmesini önler.
SameSite’ın Yetmediği Yerler
Nitelik güçlü bir azaltmadır, ama tek savunma olarak yeterli sayılmaz.
Birincisi, “aynı site” ile “aynı kaynak” farklı ölçütlerdir. Aynı site ölçütü kayıt
edilebilir alan adı üzerinden hesaplanır; rapor.ornek.test ile olcum.ornek.test ayrı
kaynaklardır ama aynı sitedir. Alt alanlardan birinin denetimini kaybetmek, o alan
üzerinden gönderilen isteklerin aynı site sayılması demektir. Bu riski daraltmak için
çerez adına __Host- öneki konur: bu önek, çerezin yalnız tam olarak belirlenen konağa
ait olmasını ve alan adı kapsamının genişletilememesini zorunlu kılar.
İkincisi, niteliğin bildirilmediği durumda uygulanacak davranış gerçekleştirime bağlıdır. Bazı gerçekleştirimler bildirilmemiş çerezi kısıtlayıcı bir değer gibi ele alır, bazıları almaz. Bu yüzden nitelik varsayılana bırakılmaz, açıkça yazılır.
Üçüncüsü, Lax değeri üst düzey GET gezinmesini geçirir. Durum değiştiren bir işlem
GET ile yapılıyorsa bu savunma o işlemi kapsamaz. Yöntem disiplini bu yüzden savunmanın
parçasıdır.
İkinci Katman: Oturuma Bağlı Belirteç
İkinci savunma, isteğin gerçekten uygulamanın kendi sayfasından başlatıldığını kanıtlar. Sunucu her oturum için tahmin edilemez bir istek belirteci üretir, sayfaya gömer ve gönderimde geri bekler. Belirtecin oturuma bağlanması kritiktir: bağlanmazsa saldırgan kendi oturumu için aldığı geçerli bir belirteci başka bir kullanıcının isteğine yerleştirebilir.
// belirtec.mjs — oturuma baglanmis istek belirteci uretimi ve dogrulanmasi import { createHmac, timingSafeEqual } from 'node:crypto'; const SUNUCU_GIZLISI = Buffer.from('7b2a0f5c9d1e4a8b6c3f0d2e5a7b9c1d', 'utf8'); function belirtecUret(oturumKimligi, tuz) { const imza = createHmac('sha256', SUNUCU_GIZLISI).update(`${oturumKimligi}.${tuz}`).digest('base64url'); return `${tuz}.${imza}`; } function belirtecDogrula(oturumKimligi, belirtec) { if (typeof belirtec !== 'string' || !belirtec.includes('.')) return false; const [tuz] = belirtec.split('.'); const beklenen = Buffer.from(belirtecUret(oturumKimligi, tuz)); const gelen = Buffer.from(belirtec); return beklenen.length === gelen.length && timingSafeEqual(beklenen, gelen); } function kaynakDenetimi(basliklar, beklenen) { if (basliklar['sec-fetch-site'] === 'same-origin') return true; if (basliklar.origin) return basliklar.origin === beklenen; return false; } const OTURUM = 'oturum-4417'; const gecerli = belirtecUret(OTURUM, 'a1b2c3'); console.log('uretilen belirtec:', gecerli); const denemeler = [ ['dogru oturum, dogru belirtec', OTURUM, gecerli], ['baska oturumun belirteci', 'oturum-9002', gecerli], ['imzasi degistirilmis', OTURUM, `${gecerli.slice(0, -1)}X`], ['belirtec yok', OTURUM, undefined], ]; console.log('\nbelirtec dogrulama:'); for (const [ad, oturum, deger] of denemeler) { console.log(' ', ad.padEnd(30), belirtecDogrula(oturum, deger) ? 'gecerli' : 'reddedildi'); } const BEKLENEN_KAYNAK = 'https://olcum.ornek.test'; const baslikKumeleri = [ ['kendi sayfasindan', { origin: BEKLENEN_KAYNAK, 'sec-fetch-site': 'same-origin' }], ['baslik yok, isaret var', { 'sec-fetch-site': 'same-origin' }], ['disaridan gonderim', { origin: 'https://baska.ornek.test', 'sec-fetch-site': 'cross-site' }], ['ikisi de yok', {}], ]; console.log('\nkaynak denetimi:'); for (const [ad, basliklar] of baslikKumeleri) { console.log(' ', ad.padEnd(30), kaynakDenetimi(basliklar, BEKLENEN_KAYNAK) ? 'kabul' : 'reddedildi'); }
$ node belirtec.mjs uretilen belirtec: a1b2c3.F5jj3rvIaTbTm94oBnF_AttfP9Oumrx8um4rO-DYY64 belirtec dogrulama: dogru oturum, dogru belirtec gecerli baska oturumun belirteci reddedildi imzasi degistirilmis reddedildi belirtec yok reddedildi kaynak denetimi: kendi sayfasindan kabul baslik yok, isaret var kabul disaridan gonderim reddedildi ikisi de yok reddedildi
Belirteç, sunucu gizlisiyle üretilen bir imza taşır ve imza oturum kimliğini kapsar; başka
bir oturumun belirteci sunulduğunda imza uyuşmaz. Karşılaştırma timingSafeEqual ile
yapılır: karakter karakter kısa devre yapan bir karşılaştırma, doğru önek uzunluğunu
ölçülebilir bir zaman farkına dönüştürür.
Belirtecin taşınma yolu da kuralın parçasıdır. Yalnız bir çerezde taşınırsa saldırı karşısında değeri kalmaz, çünkü çerez de kendiliğinden gider. Belirteç, çerez dışında bir yerde bulunmalıdır: gizli bir form alanı veya uygulamanın açıkça eklediği bir istek başlığı. Uygulama Mimarisi kursundaki Tarayıcıda Kimlik Doğrulama dersi bu ayrımın öteki yüzünü kurmuştu; oturumu bir çerez yerine bellekteki bir belirteçle taşıyan ve isteğe açıkça ekleyen bir tasarımda ortam yetkisi zaten oluşmaz, dolayısıyla bu saldırı sınıfı kendiliğinden ortadan kalkar. Bedeli, oturumun sayfa yenilemesinde yeniden kurulmasıdır.
Üçüncü Katman: Kaynak Denetimi
Çıktının son bölümü üçüncü katmanı gösterir. Sunucu, durum değiştiren her istekte isteğin
kaynağını denetler. Origin başlığı bulunuyorsa beklenen kaynakla karşılaştırılır. Bu
başlığın bulunmadığı durumlar için tarayıcının eklediği getirme meta bilgisi kullanılır:
Sec-Fetch-Site başlığı isteğin aynı kaynaktan mı, aynı siteden mi, yoksa dışarıdan mı
başlatıldığını bildirir.
Denetimin son satırı önemlidir: iki başlığın da bulunmadığı istek kabul edilmez. Eksikliği “sorun yok” saymak, denetimi bir başlığın silinmesiyle atlatılabilir hâle getirir. Bu başlıklar uygulama kodunun değil tarayıcının yazdığı başlıklardır ve tarayıcı tarafından değiştirilemez; yokluklarını güvenli yorumlamak için bir neden yoktur.
Yöntem Disiplini
Bütün bu katmanların altında bir varsayım vardır: durum değiştiren işlemler güvenli
yöntemlerle yapılmaz. İnternet Nasıl Çalışır kursunda tanımlanan güvenli yöntem
kavramı burada bir güvenlik gereğine dönüşür. Ölçüm kaydını silen bir uç GET ile
çağrılabiliyorsa, bir görsel ögesi bile o ucu tetikler; Lax çerez davranışı ve pek çok
kaynak denetimi bu durumu kapsamaz.
Kural olarak yazma işlemleri POST, PUT, PATCH veya DELETE yöntemleriyle yapılır ve
sunucu, yazma uçlarında yöntem denetimini açıkça uygular.
Özet
- İstek sahteciliği, çerezin isteği kimin başlattığına bakılmadan eklenmesinden doğar; yanıtın okunamaması yan etkinin gerçekleşmesini engellemez.
SameSiteniteliği ilk katmandır:Strictkaynaklar arası her isteği,Laxüst düzey güvenli gezinme dışındaki her isteği eler;NoneiçinSecurezorunludur.- Aynı site ölçütü aynı kaynak ölçütünden geniştir; alt alan riski
__Host-öneki ve ikinci bir katmanla daraltılır. - İstek belirteci oturuma bağlanır, çerez dışında bir yerde taşınır ve sabit zamanlı karşılaştırmayla doğrulanır.
OriginveSec-Fetch-Sitebaşlıkları üçüncü katmandır; ikisinin de bulunmadığı istek reddedilir ve durum değiştiren işlemler güvenli yöntemlere açılmaz.
Sonraki Adım
Bu dersin savunmaları tek tek başlıklar ve nitelikler üzerinden kuruldu. Aynı yanıt başlığı yüzeyinde, saldırı sınıfına özgü olmayan ama tümünün etkisini azaltan bir küme daha vardır: bağlantının şifresiz kalmasını engelleyen, belgenin başka bir sayfa tarafından çerçevelenmesini sınırlayan ve dışarı sızan adres bilgisini daraltan başlıklar. Sonraki ders bu kümeyi ve eksikliklerini bulan bir yapılandırma denetimini ele alır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.