İçeriğe geç
academia.sh

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.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat