İçeriğe geç
academia.sh

Ders 04 / 22

Rota Koruma

Yetkiye bağlı erişim; yol koruyucularının rota ağacında kalıtılması, izin–yönlendir–beklet kararları, bilinmeyen kimlik durumunun ayrı ele alınması, dönüş adresinin doğrulanması ve istemci denetiminin sınırı.

İçindekiler

Adres artık hangi kaynağın hangi görünümünün istendiğini eksiksiz taşıyor. Taşımadığı tek şey, kullanıcının o kaynağı görmeye hakkı olup olmadığı. Kuzey Yamaç Ölçüm İstasyonu uygulamasında istasyon ayarları ekranı yalnız yetkili kullanıcılara, ölçüm girişi yalnız oturum açmış kullanıcılara açık.

Bağlantıyı gizlemek çözüm değildir: adres çubuğuna elle yazılan bir yol da eşleyiciye ulaşır. Karar, eşleşme ile görünümün kurulması arasında verilmelidir. Bu ders o karar noktasını, kararın türlerini ve istemci tarafında verilen kararın neyi sağlayıp neyi sağlamadığını ele alır.

Yol Koruyucu

Yol koruyucu, bir rotanın görünümü kurulmadan önce çalışan ve üç yanıttan birini veren işlevdir. Girdisi eşleşen rota ile o anki kimlik bilgisidir; çıktısı bir karardır.

İzin, görünümün kurulmasına engel olmaz. Yönlendir, gezinmeyi başka bir adrese çevirir. Beklet, kararın henüz verilemediğini söyler; kimlik bilgisi gelene kadar ne hedef görünüm ne de yönlendirme kurulur.

Koruyucular rota ağacında kalıtılır. Bir düğüme konan koruyucu, o düğümün altındaki tüm rotalar için geçerlidir. İç İçe Rotalar ve Düzenler dersindeki yolsuz düzen düğümlerinin ikinci kullanımı buydu: aynı erişim koşulunu paylaşan rotalar tek bir üst düğümün altında toplanır ve koşul bir kez yazılır.

Bir rotanın koruyucuları kökten yaprağa doğru sıralanmış bir zincir oluşturur ve sırayla çalıştırılır. İlk reddeden kararı verir; kalan koruyucular çalışmaz. Sıra rastgele değildir: oturum koşulu rol koşulundan önce gelir, çünkü rolü sorabilmek için önce kullanıcının kim olduğunun bilinmesi gerekir.

// yol-koruyucu.mjs — kalitilan koruyucu zinciri, karar turleri ve donus adresinin dogrulanmasi
const KORUYUCU = {
  oturum: (k) => k.durum === "bilinmiyor" ? { karar: "beklet" }
    : k.durum === "acik" ? { karar: "izin" }
      : { karar: "yonlendir", hedef: "/oturum" },
  yonetici: (k) => k.rol === "yonetici" ? { karar: "izin" }
    : { karar: "yonlendir", hedef: "/yetkisiz", kayitDegistir: true },
  anonim: (k) => k.durum === "acik" ? { karar: "yonlendir", hedef: "/istasyon", kayitDegistir: true }
    : { karar: "izin" },
};

// Koruyucular rota agacinda kalitilir: ustteki dugumunki alt agacin tumune uygulanir.
const AGAC = [
  { yol: "/", koruyucu: [] },
  { yol: "/oturum", koruyucu: ["anonim"] },
  { yol: "/istasyon", koruyucu: ["oturum"] },
  { yol: "/istasyon/:kimlik/olcum", koruyucu: ["oturum"] },
  { yol: "/istasyon/:kimlik/ayar", koruyucu: ["oturum", "yonetici"] },
];

const TABAN = "https://ornek.gecerli";

function degerlendir(rota, kullanici, istenenAdres) {
  for (const ad of rota.koruyucu) {
    const sonuc = KORUYUCU[ad](kullanici);
    if (sonuc.karar === "izin") continue;
    if (sonuc.karar === "beklet") return { karar: "beklet", koruyucu: ad };
    const hedef = sonuc.hedef === "/oturum"
      ? `/oturum?donus=${encodeURIComponent(istenenAdres)}`
      : sonuc.hedef;
    return { karar: "yonlendir", koruyucu: ad, hedef, kayitDegistir: sonuc.kayitDegistir ?? true };
  }
  return { karar: "izin" };
}

const kullanicilar = [
  { ad: "yukleniyor", durum: "bilinmiyor", rol: null },
  { ad: "konuk", durum: "kapali", rol: null },
  { ad: "gozlemci", durum: "acik", rol: "gozlemci" },
  { ad: "yonetici", durum: "acik", rol: "yonetici" },
];

console.log("-- karar tablosu --");
for (const k of kullanicilar) {
  for (const rota of AGAC) {
    const istenen = rota.yol.replace(":kimlik", "kuzey-yamac");
    const s = degerlendir(rota, k, istenen);
    console.log([
      k.ad.padEnd(11), istenen.padEnd(30), s.karar.padEnd(10),
      (s.hedef ?? "-").padEnd(48), s.koruyucu ? `(${s.koruyucu})` : "",
    ].join(" ").trimEnd());
  }
}

// Donus adresi kullanicidan gelir; ac yonlendirmeyi onlemek icin dogrulanir.
function donusuCoz(ham) {
  if (ham === null) return "/istasyon";
  let coz;
  try { coz = new URL(ham, TABAN); } catch { return "/istasyon"; }
  if (coz.origin !== TABAN) return "/istasyon";
  if (!ham.startsWith("/") || ham.startsWith("//")) return "/istasyon";
  return coz.pathname + coz.search;
}

console.log("-- donus adresinin dogrulanmasi --");
for (const ham of [
  "/istasyon/kuzey-yamac/ayar?olcum=nem",
  "https://kotu.ornek/topla",
  "//kotu.ornek/topla",
  "istasyon/kuzey-yamac",
  "/istasyon/../../etc",
  null,
]) console.log(JSON.stringify(ham).padEnd(40), "->", donusuCoz(ham));
-- karar tablosu --
yukleniyor  /                              izin       -
yukleniyor  /oturum                        izin       -
yukleniyor  /istasyon                      beklet     -                                                (oturum)
yukleniyor  /istasyon/kuzey-yamac/olcum    beklet     -                                                (oturum)
yukleniyor  /istasyon/kuzey-yamac/ayar     beklet     -                                                (oturum)
konuk       /                              izin       -
konuk       /oturum                        izin       -
konuk       /istasyon                      yonlendir  /oturum?donus=%2Fistasyon                        (oturum)
konuk       /istasyon/kuzey-yamac/olcum    yonlendir  /oturum?donus=%2Fistasyon%2Fkuzey-yamac%2Folcum  (oturum)
konuk       /istasyon/kuzey-yamac/ayar     yonlendir  /oturum?donus=%2Fistasyon%2Fkuzey-yamac%2Fayar   (oturum)
gozlemci    /                              izin       -
gozlemci    /oturum                        yonlendir  /istasyon                                        (anonim)
gozlemci    /istasyon                      izin       -
gozlemci    /istasyon/kuzey-yamac/olcum    izin       -
gozlemci    /istasyon/kuzey-yamac/ayar     yonlendir  /yetkisiz                                        (yonetici)
yonetici    /                              izin       -
yonetici    /oturum                        yonlendir  /istasyon                                        (anonim)
yonetici    /istasyon                      izin       -
yonetici    /istasyon/kuzey-yamac/olcum    izin       -
yonetici    /istasyon/kuzey-yamac/ayar     izin       -
-- donus adresinin dogrulanmasi --
"/istasyon/kuzey-yamac/ayar?olcum=nem"   -> /istasyon/kuzey-yamac/ayar?olcum=nem
"https://kotu.ornek/topla"               -> /istasyon
"//kotu.ornek/topla"                     -> /istasyon
"istasyon/kuzey-yamac"                   -> /istasyon
"/istasyon/../../etc"                    -> /etc
null                                     -> /istasyon

Tablonun her satırı bir çift karardır: kim, nereye. Gözlemci ölçüm geçmişini görür ama ayarlara giremez; yönetici ikisine de girer. Kök yol ve oturum ekranı koruyucusuz ya da ters koruyuculudur.

Ters Koruyucu

anonim koruyucusu diğerlerinin tersini yapar: oturumu açık olan kullanıcıyı oturum ekranından uzaklaştırır. Bunu yapmayan bir uygulamada, oturum açmış kullanıcı geri düğmesiyle oturum ekranına döner ve orada ne yapacağını bilmez; yeniden oturum açmaya kalkarsa kimlik akışı iki kez çalışır.

Aynı ters mantık kayıt, parola sıfırlama ve karşılama akışları için de geçerlidir.

Bilinmeyen Kimlik

Üçüncü karar türü, çıktının ilk beş satırında görülür. Uygulama yeni yüklendiğinde oturumun açık olup olmadığı henüz bilinmez: bu bilgi ya bir istekle sunucudan gelir ya da yerel bir kayıttan okunur. Her ikisi de zaman alır.

Bu aralıkta yapılan en yaygın hata, bilinmeyeni yetkisiz saymaktır. Sonucu tanıdıktır: kullanıcı korunan bir sayfayı yeniler, bir an oturum ekranını görür, kimlik bilgisi gelince asıl sayfaya döner. Ekranın bu şekilde iki kez değişmesi yalnız rahatsız edici değildir; adres de iki kez değişmiş, geçmişe gereksiz kayıt girmiş olabilir.

Doğru davranış, kararı ertelemektir. Beklet yanıtı alan yönlendirme, kimlik bilgisi gelene kadar ne hedef görünümü ne de oturum ekranını kurar; yerinde bir yükleniyor göstergesi bırakır. Kimlik geldiğinde koruyucu yeniden çalıştırılır ve gerçek karar verilir.

Bekletmenin bir sınırı olmalıdır. Kimlik isteği başarısız olursa karar sonsuza kadar ertelenemez; başarısızlık yetkisizlikle aynı sonuca bağlanır ve kullanıcıya nedeni söylenir.

Dönüş Adresi

Konuk kullanıcı korunan bir adrese gittiğinde oturum ekranına gönderilir. Oturum açtıktan sonra karşılama ekranına düşmesi, gitmek istediği yeri unutmak demektir. Bu nedenle istenen adres yönlendirmenin sorgusuna yazılır ve oturum açıldıktan sonra oradan okunur.

Bu parametre kullanıcının denetimindedir. Bir saldırgan donus değerine kendi adresini koyup bağlantıyı paylaşabilir; kullanıcı tanıdığı bir sitede oturum açar ve hemen ardından başka bir yere gönderilir. Bu kusurun adı açık yönlendirmedir ve önlemi tek cümledir: dönüş adresi kullanılmadan önce uygulamanın kendi kaynağına ait olduğu doğrulanır.

Çıktının son bölümü doğrulamanın ne yakaladığını gösterir. Tam nitelikli dış adres reddedilir. Eğik çizgiyle başlayan protokolsüz adres — tarayıcının aynı protokolle dış bir konağa gitmesini sağlayan biçim — reddedilir; bu, yalnız ilk karakteri sınayan bir denetimin kaçırdığı durumdur. Göreli adres de reddedilir, çünkü hangi tabana göre çözüleceği belirsizdir. Geriye yalnız uygulama içi yollar kalır ve bunlar çözülürken normalleştirilir.

Aynı sınama, adresi kullanıcıdan alan her yerde geçerlidir: sorguda taşınan hedef adresler, bildirim bağlantıları, hata sayfasındaki dönüş düğmeleri.

Yönlendirme Geçmişe Nasıl Yazılır

Koruyucunun ürettiği yönlendirme, kullanıcının istediği bir gezinme değildir. Bu nedenle geçmişe yeni bir kayıt eklemez, o anki kaydın yerine geçer. Aksi hâlde kullanıcı oturum ekranından geri düğmesine bastığında korunan adrese döner, koruyucu yeniden çalışır ve onu tekrar oturum ekranına gönderir — geri düğmesi işlemez hâle gelir.

Örnekteki kararlar bu nedenle kayitDegistir bayrağı taşır. Geçmiş ve Konum dersinde kurulan ayrım burada doğrudan uygulanır: kullanıcının geri dönmek isteyeceği durumlar eklenir, ara adresler değiştirilir.

İstemci Denetiminin Sınırı

Bu dersin kurduğu her şey arayüz kararıdır, güvenlik sınırı değildir. Üç gerekçe bunu gösterir.

Koruyucunun kodu kullanıcının makinesinde çalışır ve okunabilir; rol adları, koşullar, gizli rotaların yolları paketin içindedir. Kullanıcı koruyucuyu çalıştırmayabilir: adresi doğrudan istemek, isteği elle üretmek ya da yanıtı değiştirmek elindedir. Korunan görünümün kullandığı veri de yine bir istekle alınır; görünümü gizlemek veriyi gizlemez.

Bu nedenle her korunan işlem sunucuda yeniden yetkilendirilir. İstemci tarafı koruma, yetkisiz bir kullanıcının boş bir ekranla ya da bir hata yığınıyla karşılaşmasını önler; verinin korunmasını sağlamaz.

Aynı ayrım gizli bilgi için de geçerlidir. Rol bilgisine göre bir menü ögesini gizlemek kabul edilebilir; ancak yalnız yöneticinin görmesi gereken bir veri, gizlenmiş bir bileşenin içine konup istemciye gönderilemez. Gönderilen her şey görülebilir.

Özet

  • Yol koruyucu, eşleşme ile görünümün kurulması arasında çalışır ve izin, yönlendir ya da beklet kararlarından birini verir; koruyucular rota ağacında kalıtılır, zincir hâlinde sırayla çalışır ve ilk reddeden karar verir.
  • Kimliğin henüz bilinmediği durum yetkisizlikten ayrılır; karar ertelenir, ama erteleme başarısızlık durumunda sonlandırılır.
  • Ters koruyucu, oturumu açık kullanıcıyı oturum ve kayıt ekranlarından uzaklaştırır.
  • Dönüş adresi kullanıcı girdisidir; uygulamanın kendi kaynağına ait olduğu doğrulanmadan kullanılırsa açık yönlendirme kusuru doğar.
  • Koruyucunun ürettiği yönlendirme geçmişe kayıt eklemez, o anki kaydı değiştirir.
  • İstemci tarafı koruma bir arayüz kararıdır; her korunan işlem ve veri sunucuda yeniden yetkilendirilir.

Sonraki Adım

Yönlendirme artık hangi ekranın kime açık olduğunu biliyor. Bilmediği şey, o ekranın kodunun ne zaman indirileceği. Şu anda uygulamanın bütün ekranları — istasyon listesi, ayarlar formu, ölçüm grafiği, oturum akışı — tek bir pakette birlikte iniyor. Kullanıcı yalnız oturum açmak için gelse bile yönetici ayar ekranının kodunu indiriyor. Rota ağacı bu bölünmeyi yapmak için gereken bilgiyi zaten taşıyor: hangi bileşenin hangi yolda kullanıldığı tanımın içinde yazılı. Sonraki ders paketi rota sınırlarından bölmeyi, ortak parçaların yinelenmesini önlemeyi ve bölünmenin getirdiği bekleme ile hata durumlarını 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