Ders 20 / 27
Yaygın Web Riskleri
Sık görülen açık sınıflarının istemci tarafındaki karşılıkları; arayüzde verilen yetki kararları, açık yönlendirme, istemciye ulaşan veriye duyulan güven ve pakete sızan sırlar.
İçindekiler
Bu konudaki altı ders belirli mekanizmalar kurdu: çıkışta kaçırma, içerik güvenlik politikası, kaynaklar arası paylaşım kuralları, istek belirteçleri, güvenlik başlıkları ve alt kaynak bütünlüğü. Her biri adı konmuş bir saldırı sınıfına karşılık geliyordu.
Uygulamada karşılaşılan sorunların bir bölümü bu sınıfların dışında kalır ve ortak bir kökene sahiptir: istemcinin bir güvenlik sınırı sanılması. Bu ders, sık görülen açık sınıflarını istemci tarafındaki izdüşümleriyle ele alır ve her biri için ölçülebilir bir kural verir.
Arayüzde Gizlemek Yetki Vermemekle Aynı Şey Değil
Kuzey Yamaç Ölçüm İstasyonu arayüzünde ölçüm kaydını düzenleme düğmesi yalnız düzenleyici rolündeki kullanıcılara gösteriliyor. Bu doğru bir arayüz kararıdır ve yanlış bir güvenlik kararıdır. Düğmenin görünmemesi, düğmenin tetiklediği isteğin gönderilemeyeceği anlamına gelmez; istek bir adres ve bir gövdeden ibarettir.
// yetki.mjs — istemci gizlemesi ile sunucu yetkilendirmesinin bagimsizligi function arayuzGosterir(kullanici) { return kullanici.roller.includes('duzenleyici'); } function sunucuIzinVerir(oturum, kayit) { if (!oturum.dogrulandi) return false; if (!oturum.roller.includes('duzenleyici')) return false; return kayit.istasyon === oturum.istasyon; } const kayit = { istasyon: 'kuzey-yamac', olcumNo: 4417 }; const durumlar = [ ['duzenleyici, kendi istasyonu', { dogrulandi: true, roller: ['duzenleyici'], istasyon: 'kuzey-yamac' }], ['duzenleyici, baska istasyon', { dogrulandi: true, roller: ['duzenleyici'], istasyon: 'guney-sirt' }], ['yalniz okuyucu', { dogrulandi: true, roller: ['okuyucu'], istasyon: 'kuzey-yamac' }], ['dogrulanmamis', { dogrulandi: false, roller: ['duzenleyici'], istasyon: 'kuzey-yamac' }], ]; console.log('durum'.padEnd(30), 'dugme gorunur', 'istek kabul'); for (const [ad, oturum] of durumlar) { const gorunur = arayuzGosterir({ roller: oturum.roller }) ? 'evet' : 'hayir'; const kabul = sunucuIzinVerir(oturum, kayit) ? 'evet' : 'hayir'; console.log(ad.padEnd(30), gorunur.padEnd(14), kabul); }
$ node yetki.mjs durum dugme gorunur istek kabul duzenleyici, kendi istasyonu evet evet duzenleyici, baska istasyon evet hayir yalniz okuyucu hayir hayir dogrulanmamis evet hayir
İki sütunun ayrı ayrı hesaplanması bilerek yapıldı. İkinci ve dördüncü satırlar, arayüzün “evet” dediği ama sunucunun “hayır” dediği durumlardır; arayüz tarafındaki karar yalnız rolü görür, sunucu tarafındaki karar kaydın hangi istasyona ait olduğunu ve oturumun doğrulanıp doğrulanmadığını da görür.
Bu ayrım iki alt sınıfın kökenidir. Bozuk nesne düzeyinde erişim denetimi, bir kaydın tanımlayıcısı değiştirildiğinde sunucunun sahiplik denetimi yapmamasıdır; arayüz o kaydı hiç göstermediği için sorun geç fark edilir. Dikey yetki yükselmesi, yalnız arayüzde gizlenmiş bir yönetim işleminin doğrudan çağrılabilmesidir. İkisinin de savunması aynı cümledir: her istek, isteği yapanın kim olduğuna ve neye eriştiğine bakılarak sunucuda yeniden değerlendirilir.
Aynı kural doğrulamaya da uygulanır. Formların istemci tarafındaki kısıt doğrulaması bir kullanılabilirlik özelliğidir; hatayı kullanıcıya erken gösterir. Veri bütünlüğünü sağlayan denetim sunucudakidir ve istemci tarafındaki kuralın varlığı onu gereksizleştirmez.
Açık Yönlendirme
Giriş akışı, kullanıcıyı doğrulamadan sonra geldiği sayfaya geri gönderir. Hedef adres genellikle sorgu dizgisinde taşınır ve doğrulanmazsa uygulama, herhangi bir adrese yönlendirme yapan bir araca dönüşür. Zararı doğrudan değildir: uygulamanın alan adı, kullanıcıyı başka bir yere göndermek için kullanılır.
// yonlendirme.mjs — gerisdonus adresini izin listesine gore dogrular const KENDI_KAYNAGI = 'https://olcum.ornek.test'; const IZINLI_KAYNAKLAR = new Set([KENDI_KAYNAGI, 'https://rapor.ornek.test']); const VARSAYILAN = KENDI_KAYNAGI + '/istasyon'; function gerisdonusHedefi(ham) { if (typeof ham !== 'string' || ham === '') return VARSAYILAN; let adres; try { adres = new URL(ham, KENDI_KAYNAGI); } catch { return VARSAYILAN; } if (!['http:', 'https:'].includes(adres.protocol)) return VARSAYILAN; if (!IZINLI_KAYNAKLAR.has(adres.origin)) return VARSAYILAN; return adres.href; } const adaylar = [ '/istasyon/kuzey-yamac?gun=12', 'https://rapor.ornek.test/ozet', 'https://baska.ornek.test/giris', '//baska.ornek.test/giris', 'https://olcum.ornek.test.baska.test/giris', 'olcum-uygulamasi://ac', '', ]; for (const aday of adaylar) { console.log(gerisdonusHedefi(aday).padEnd(52), '<-', aday === '' ? '(bos)' : aday); }
$ node yonlendirme.mjs https://olcum.ornek.test/istasyon/kuzey-yamac?gun=12 <- /istasyon/kuzey-yamac?gun=12 https://rapor.ornek.test/ozet <- https://rapor.ornek.test/ozet https://olcum.ornek.test/istasyon <- https://baska.ornek.test/giris https://olcum.ornek.test/istasyon <- //baska.ornek.test/giris https://olcum.ornek.test/istasyon <- https://olcum.ornek.test.baska.test/giris https://olcum.ornek.test/istasyon <- olcum-uygulamasi://ac https://olcum.ornek.test/istasyon <- (bos)
Dördüncü ve beşinci satırlar denetimin neden dizgi karşılaştırmasıyla yazılamayacağını
gösterir. Şema olmadan iki bölü işaretiyle başlayan bir adres, sayfanın şemasını devralarak
başka bir konağa gider; “kendi alan adımızla başlıyor” ölçütü ise ornek.test.baska.test
adını geçirir. Karşılaştırma çözümlenmiş adresin origin bileşeni üzerinde yapılır ve
doğrulanamayan her girdi izin listesine değil, sabit bir varsayılana düşer.
İstemciye Ulaşan Veriye Duyulan Güven
Yerel depolamadan okunan bir tercih kaydı, adres parçasından çözülen bir durum veya bir uçtan gelen yapılandırma nesnesi, uygulamanın kendi ürettiği veri değildir. Bu veriyi uygulama nesnelerine birleştiren kod, anahtar adlarının da veri olduğunu varsaymalıdır.
// birlestirme.mjs — disaridan gelen ayarlari prototip zincirine dokunmadan birlestirir const TEHLIKELI_ANAHTARLAR = new Set(['__proto__', 'prototype', 'constructor']); function guvenliBirlestir(taban, gelen) { const sonuc = Object.create(null); for (const [anahtar, deger] of Object.entries(taban)) sonuc[anahtar] = deger; for (const [anahtar, deger] of Object.entries(gelen)) { if (TEHLIKELI_ANAHTARLAR.has(anahtar)) { console.log(' atlanan anahtar:', anahtar); continue; } sonuc[anahtar] = deger; } return sonuc; } const varsayilan = { birim: 'santigrat', esik: -5, otomatikYenile: true }; const kayittanGelen = JSON.parse('{"esik": -12, "__proto__": {"yonetici": true}, "birim": "kelvin"}'); console.log('birlestirme:'); const ayarlar = guvenliBirlestir(varsayilan, kayittanGelen); console.log('sonuc:', JSON.stringify(ayarlar)); console.log('prototip zinciri:', Object.getPrototypeOf(ayarlar)); console.log('sirada olmayan nesnede yonetici alani:', ({}).yonetici);
$ node birlestirme.mjs
birlestirme:
atlanan anahtar: __proto__
sonuc: {"birim":"kelvin","esik":-12,"otomatikYenile":true}
prototip zinciri: null
sirada olmayan nesnede yonetici alani: undefined
JavaScript’te Nesneler ve Fonksiyonlar kursunda tanıtılan prototip zinciri burada bir saldırı yüzeyine dönüşür: ortak bir prototipe yazılan bir alan, o prototipten türeyen bütün nesnelerde görünür. Savunma iki parçalıdır. Birleştirme sırasında zincire ulaşan anahtarlar atlanır ve hedef nesne prototipsiz nesne olarak oluşturulur; son satır, ilgisiz bir nesnenin etkilenmediğini doğrular.
Aynı ilkenin başka bir uygulaması, dış veriyi bir şemaya karşı doğrulamaktır. Uygulama Mimarisi kursundaki doğrulama şemaları dersinde kurulan yaklaşım burada güvenlik değeri kazanır: beklenen alanları sayan bir şema, beklenmeyen anahtarları yapısal olarak eler.
Pakete Sızan Sırlar
İstemciye gönderilen her şey herkese açıktır. Bu cümle bir uyarı değil, bir tanımdır: paket dosyası indirilebilir, ağ paneli açılabilir, kaynak haritası okunabilir.
Buradan üç kural çıkar. Bir dış hizmetin gizli anahtarı istemci paketine konmaz; istemcinin gerektirdiği çağrılar kendi sunucusu üzerinden yapılır. Yalnız istemci tarafında anlamlı olan yapılandırma ile sunucu tarafındaki sır ayrı yerlerde tutulur ve derleme aracının istemci paketine hangi değişkenleri gömdüğü açıkça denetlenir. Kaynak haritalarının üretim ortamında herkese açık sunulup sunulmayacağı bilinçli bir karardır; hata izleme için gerekiyorsa, tarayıcıya değil hata toplama katmanına verilir.
Aynı kural hata yüzeyine de uygulanır. İstemciye dönen hata iletileri iç yapı bilgisi taşımaz; istemciden toplanan hata kayıtları ise kullanıcı verisi taşıyabilir ve toplanmadan önce ayıklanmalıdır.
Sınıfları Bir Arada Okumak
Bu konudaki yedi dersin ortak dokusu tek bir ölçütte toplanır: bir denetimin nerede çalıştığı, o denetimin ne kadar güvenilebilir olduğunu belirler. İstemcide çalışan her denetim kullanıcı deneyimine hizmet eder ve kullanıcının denetimindedir. Tarayıcının uyguladığı denetimler (aynı kaynak politikası, içerik güvenlik politikası, çerez nitelikleri, bütünlük doğrulaması) sunucunun bildirdiği kurallara dayanır ve kullanıcının tarayıcısını korur. Sunucuda çalışan denetimler ise uygulamanın verisini korur ve diğerlerinin hiçbiri onların yerine geçmez.
Üç katmanın karıştırılması, bu konuda anlatılan açık sınıflarının çoğunun ortak nedenidir.
Özet
- Arayüzde bir denetimi gizlemek yetkilendirme değildir; her istek sunucuda, isteği yapanın kimliğine ve eriştiği kayda bakılarak yeniden değerlendirilir.
- İstemci tarafındaki kısıt doğrulaması kullanılabilirlik içindir; veri bütünlüğünü sunucudaki doğrulama sağlar.
- Yönlendirme hedefi çözümlenmiş adresin kaynağı üzerinden izin listesine karşı sınanır; doğrulanamayan girdi sabit bir varsayılana düşer.
- Dış kaynaktan gelen nesnelerde anahtar adları da veridir; birleştirme prototip zincirine ulaşan anahtarları eler ve hedefi prototipsiz nesne olarak kurar.
- İstemciye gönderilen her şey herkese açıktır; gizli anahtarlar pakete girmez, kaynak haritalarının yayımlanması bilinçli bir karardır.
- Bir denetimin güvenilirliği nerede çalıştığına bağlıdır: istemci, tarayıcı ve sunucu katmanları birbirinin yerine geçmez.
Sonraki Adım
Bu konu boyunca kurulan savunmaların ortak bir zayıflığı vardır: hepsi sessizce bozulabilir. Bir kaçırma noktası atlanır, bir politika yönergesi gevşetilir, bir çerez niteliği düşer, bir bütünlük değeri güncellenmeden bırakılır — ve arayüz her durumda çalışmayı sürdürür. Kalitenin son ekseni bu yüzden ölçmedir. Sonraki konu, davranışın beklendiği gibi kaldığını doğrulayan sınamalarla başlar: Birim ve Bileşen Testleri dersi, bileşenlerin gerçekten ne yaptığını davranış üzerinden sınamanın kurallarını kurar.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.