İçeriğe geç
academia.sh

Ders 07 / 17

Güvensiz Seri Durumdan Çıkarma

Seri durumdan çıkarmanın güvenilir tür kümesi cinsinden ölçülmesi: altı iletideki tür etiketli düğümler her derinlikte sayılır, açık tür kaydı ile izin listesinin kabul ettiği tür ve kurduğu düğüm sayısı karşılaştırılır, etiketsiz bir veri biçiminin yapı kurma noktasını sıfıra indirmesi ve karşılığında getirdiği eşleme noktası ölçülür, kod tabanına üç yeni tür eklendiğinde kabul kümesinin nasıl değiştiği çıkarılır.

İçindekiler

Önceki altı ders dışarıdan gelen şeyi hep bir değer olarak ele aldı: bir metin, bir sayı, bir bayt dizisi, bir adres. Her seferinde savunma, o değerin gideceği yerin kuralını bilerek kuruldu. Kimi akışlarda gelen şey değer değildir: alanlarının yanında türü de verinin içinde yazar ve alıcı taraf o türü okuyup karşılık gelen yapıyı kurar.

Bu ders o kurma anını sayar. Ölçü birimi güvenilir tür kümesidir: kaç tür kabul ediliyor, izin listesi kaçını kapatıyor ve türü verinin içinden okumayan bir veri biçimi kaç noktayı ortadan kaldırıyor.

Tür Etiketli Biçim

Model bir kayıt tutar: uygulamanın tanıdığı türler ve her türü kuran işlev. Gelen iletide $tur alanı hangi kaydın çağrılacağını söyler. Kayıttaki türlerin bir kısmı yalnız veri taşır; bir kısmı kurulurken bir kaynak açar ya da bir iş başlatır.

GC15 (varsayım): yan etki işareti bildirimdir; bu derste hiçbir tür gerçekten bir şey çalıştırmaz. Ölçülen şey bir etkinin gerçekleşmesi değil, kaç türün erişilebilir olduğudur.

// bicim.mjs — tur etiketi tasiyan bir seri durumdan cikarma bicimi (kurgu model)
// yanEtki: kurulurken bir kaynak acan ya da bir is baslatan tur. Burada yalniz
// isaretlenir; hicbiri gercekten bir sey calistirmaz.
export const KAYIT = {
  Abone:        { yanEtki: false, kur: (a) => ({ tur: "Abone", no: a.no }) },
  Okuma:        { yanEtki: false, kur: (a) => ({ tur: "Okuma", deger: a.deger }) },
  Fatura:       { yanEtki: false, kur: (a) => ({ tur: "Fatura", tutar: a.tutar }) },
  IsEmri:       { yanEtki: false, kur: (a) => ({ tur: "IsEmri", no: a.no }) },
  DosyaKaynagi: { yanEtki: true,  kur: (a) => ({ tur: "DosyaKaynagi", yol: a.yol }) },
  Zamanlayici:  { yanEtki: true,  kur: (a) => ({ tur: "Zamanlayici", her: a.her }) },
  DisCagri:     { yanEtki: true,  kur: (a) => ({ tur: "DisCagri", hedef: a.hedef }) },
  Gunlukcu:     { yanEtki: true,  kur: (a) => ({ tur: "Gunlukcu", kanal: a.kanal }) },
  Onbellek:     { yanEtki: true,  kur: (a) => ({ tur: "Onbellek", boyut: a.boyut }) },
};
export const IZIN = ["Abone", "Okuma", "Fatura", "IsEmri"];

// Gelen iletiler (kurgu): ucu sistemin gercekten alisverisini yaptigi bicimler.
export const MESAJ = [
  ["okuma yukleme", { $tur: "Okuma", deger: 412 }, "beklenen"],
  ["abone kaydi", { $tur: "Abone", no: "A-100432", ek: { $tur: "IsEmri", no: 12 } }, "beklenen"],
  ["fatura ozeti", { $tur: "Fatura", tutar: 512, kalem: [{ $tur: "Okuma", deger: 5 }] }, "beklenen"],
  ["gecmis alimi", { $tur: "Okuma", deger: 7, kaynak: { $tur: "DosyaKaynagi", yol: "d1" } }, "beklenmedik"],
  ["toplu is", { $tur: "IsEmri", no: 3,
    is: { $tur: "Zamanlayici", her: 60, adim: { $tur: "DisCagri", hedef: "h" } } }, "beklenmedik"],
  ["ayar tasima", { $tur: "Onbellek", boyut: 100 }, "beklenmedik"],
];

// Bir iletideki butun tur etiketlerini, hangi derinlikte olursa olsun toplar.
export const etiketler = (o) =>
  o === null || typeof o !== "object" ? []
    : (Array.isArray(o) ? [] : o.$tur ? [o.$tur] : []).concat(Object.values(o).flatMap(etiketler));

Etiket toplayıcı ölçünün ilk yarısıdır. Bir iletideki tür kararı üst düzeyde bir tane değildir; ileti bir ağaçtır ve her düğüm kendi türünü söyleyebilir. Kapsama sayılırken sayılacak birim ileti değil, etiket taşıyan düğümdür.

Kabul Edilen Tür Kümesi

Üç çözme biçimi aynı ileti kümesine uygulanıyor. Açık kayıt tanıdığı her türü kurar. İzin listesi yalnız bildirilen dört türü kurar. Etiketsiz veri biçimi $tur alanını hiç okumaz; gelen şeyi düz veri olarak bırakır.

// kapsama.mjs — uc coz bicimi: acik kayit, izin listesi, etiketsiz veri bicimi
import { KAYIT, IZIN, MESAJ, etiketler } from "./bicim.mjs";

const BICIM = [
  ["acik kayit", Object.keys(KAYIT)],
  ["izin listesi", IZIN],
  ["etiketsiz veri bicimi", []],
];
const tumEtiket = MESAJ.flatMap(([, g]) => etiketler(g));
console.log(`${MESAJ.length} ileti, ${tumEtiket.length} tur etiketi tasiyan dugum, ` +
  `${new Set(tumEtiket).size} ayri tur\n`);
console.log(`${"coz bicimi".padEnd(24)}${"kabul tur".padStart(10)}${"kurulan dugum".padStart(14)}` +
  `${"yan etkili kurulum".padStart(20)}${"kabul ileti".padStart(12)}`);
for (const [ad, kabul] of BICIM) {
  let kurulan = 0, yanEtkili = 0, kabulIleti = 0;
  for (const [, govde] of MESAJ) {
    const e = etiketler(govde);
    const gecti = e.every((t) => kabul.includes(t));
    if (kabul.length === 0) { kabulIleti++; continue; }        // hicbir yapi kurulmaz
    if (gecti) { kabulIleti++; kurulan += e.length; yanEtkili += e.filter((t) => KAYIT[t].yanEtki).length; }
  }
  console.log(ad.padEnd(24) + String(kabul.length).padStart(10) + String(kurulan).padStart(14) +
    String(yanEtkili).padStart(20) + String(kabulIleti).padStart(12));
}
const erisilir = (k) => k.filter((t) => KAYIT[t].yanEtki).length;
console.log(`\nkayitta ${Object.keys(KAYIT).length} tur var, ${erisilir(Object.keys(KAYIT))} tanesi yan etkili`);
console.log(`izin listesi ${IZIN.length} tur kabul ediyor, ${erisilir(IZIN)} tanesi yan etkili`);
console.log(`etiketsiz bicimde yapi kurma noktasi: 0, elle esleme noktasi: ` +
  `${MESAJ.filter(([, , b]) => b === "beklenen").length}`);
6 ileti, 11 tur etiketi tasiyan dugum, 8 ayri tur

coz bicimi               kabul tur kurulan dugum  yan etkili kurulum kabul ileti
acik kayit                       9            11                   4           6
izin listesi                     4             5                   0           3
etiketsiz veri bicimi            0             0                   0           6

kayitta 9 tur var, 5 tanesi yan etkili
izin listesi 4 tur kabul ediyor, 0 tanesi yan etkili
etiketsiz bicimde yapi kurma noktasi: 0, elle esleme noktasi: 3

Açık kayıt altı iletinin altısını da kabul ediyor ve on bir düğümün on birini kuruyor; dördü yan etkili türdür. Kabul edilen tür sayısı burada bir karar değil, bir sonuçtur: kayıtta ne varsa kabul edilir.

İzin listesi kabul edilen türü dörde, kurulan düğümü beşe indiriyor ve yan etkili kurulumu sıfırlıyor. Üç ileti reddediliyor ve üçü de beklenmedik olarak etiketlenmiş iletilerdir. Reddin nedeni iletinin üst düzey türü değil, derinlikteki bir düğümdür: toplu iş iletisinin kökü izin listesindedir, iki alt düğümü değildir.

Üçüncü satır ölçünün asıl noktasıdır. Etiketsiz veri biçiminde kurulan düğüm sıfırdır ve bu bir savunmanın başarısı değildir — kurulacak bir şey yoktur. Tür kararı verinin içinden gelmiyorsa saldırıya açık bir tür seçimi de yoktur. Karşılığında son satırdaki sayı duruyor: uygulama gelen düz veriyi kendi türlerine üç ayrı yerde elle eşlemek zorundadır.

Üç biçimin kod tabanında yerleri de ayrıdır. Açık kayıt, iç servisler arasında tasarlanmış bir kolaylıktır ve dış sınırda kaldığında ölçülen sayıyı üretir. İzin listesi, tür kararının verinin içinden gelmesini kabul edip kümesini daraltır. Etiketsiz veri biçimi kararı verinin dışına çıkarır ve ölçülecek bir tür kümesi bırakmaz. Seçim bir tercih değil, hangi sınırda durulduğuyla ilgilidir: dış sınırda birinci biçimin ölçüsü kod tabanının büyüklüğüdür.

Denetimin Uygulandığı Derinlik

İzin listesi kapsamanın tamamını değil, uygulandığı yer kadarını verir. Bir ileti ağaçtır ve denetim yalnız köke uygulanabilir.

// derinlik.mjs — izin listesinin uygulandigi derinlik
import { KAYIT, IZIN, MESAJ, etiketler } from "./bicim.mjs";

const kok = (g) => (g && g.$tur ? [g.$tur] : []);
console.log(`${"denetimin uygulandigi yer".padEnd(28)}${"kabul ileti".padStart(12)}` +
  `${"kurulan dugum".padStart(14)}${"yan etkili kurulum".padStart(20)}`);
for (const [ad, sec] of [["yalniz kok dugum", kok], ["her dugum", etiketler]]) {
  let ileti = 0, kurulan = 0, yan = 0;
  for (const [, g] of MESAJ) {
    if (!sec(g).every((t) => IZIN.includes(t))) continue;
    ileti++;
    const e = etiketler(g);
    kurulan += e.length;
    yan += e.filter((t) => KAYIT[t].yanEtki).length;
  }
  console.log(ad.padEnd(28) + String(ileti).padStart(12) + String(kurulan).padStart(14) +
    String(yan).padStart(20));
}
const derin = MESAJ.filter(([, g]) => etiketler(g).length > 1);
console.log(`\n${MESAJ.length} iletinin ${derin.length} tanesi birden cok dugumde tur etiketi tasiyor`);
console.log(`en derin ileti: ${etiketler(MESAJ[4][1]).join(" -> ")}`);
denetimin uygulandigi yer    kabul ileti kurulan dugum  yan etkili kurulum
yalniz kok dugum                       5            10                   3
her dugum                              3             5                   0

6 iletinin 4 tanesi birden cok dugumde tur etiketi tasiyor
en derin ileti: IsEmri -> Zamanlayici -> DisCagri

Aynı izin listesi, yalnız kök düğüme uygulandığında beş iletiyi kabul ediyor ve on düğüm kuruyor; bunların üçü yan etkili türdür. Her düğüme uygulandığında kabul edilen ileti üçe, kurulan düğüm beşe, yan etkili kurulum sıfıra iniyor. Liste aynı listedir; değişen tek şey kaç düğümün görüldüğüdür.

Son satır nedenini gösteriyor: altı iletinin dördü birden çok düğümde tür etiketi taşıyor ve en derin ileti üç düzey iniyor. Kök düğüm denetimi bu iletilerin ilk düzeyini onaylayıp kalanını görmeden geçiriyor. Önceki dersteki yeniden yönlendirme zinciriyle aynı biçimdir: denetlenen değer ile kullanılan değer arasına bir adım girdiğinde kapsama kaybolur.

Yeni Tür Eklendiğinde

Kapsamanın sınavı, ölçüm yapıldıktan sonra kod tabanının değişmesiyle başlar. Üç yeni tür ekleniyor ve hiçbiri seri durumdan çıkarmayı düşünerek yazılmıyor.

GC16 (varsayım): kayda tür eklemek bildirim gerektirmez; bir tür tanımlandığı anda kayda girer. Ölçülen şey bu eklemenin kabul kümesine etkisidir.

// yeni-tur.mjs — kod tabanina yeni tur eklendiginde kabul kumesi nasil degisiyor
import { KAYIT, IZIN } from "./bicim.mjs";

// Uc yeni tur kod tabanina giriyor; ikisi kurulurken bir is baslatiyor.
const YENI = { RaporIsi: { yanEtki: true }, Bildirim: { yanEtki: false }, AgIstemcisi: { yanEtki: true } };
const KAYIT2 = { ...KAYIT, ...YENI };

const yanEtkili = (kayit, kume) => kume.filter((t) => kayit[t].yanEtki).length;
console.log(`${"asama".padEnd(24)}${"kayitta tur".padStart(12)}${"acik kabul".padStart(12)}` +
  `${"izinli kabul".padStart(14)}${"kapatilan tur".padStart(15)}${"erisilen yan etkili".padStart(21)}`);
for (const [ad, k] of [["mevcut kod tabani", KAYIT], ["uc yeni tur eklendi", KAYIT2]]) {
  const tum = Object.keys(k);
  console.log(ad.padEnd(24) + String(tum.length).padStart(12) + String(tum.length).padStart(12) +
    String(IZIN.length).padStart(14) + String(tum.length - IZIN.length).padStart(15) +
    `${yanEtkili(k, tum)} / ${yanEtkili(k, IZIN)}`.padStart(21));
}
console.log(`\nyeni tur icin yazilan kod: acik kayitta 0 satir (tur tanimlandigi anda kabul edilir),`);
console.log(`izin listesinde 1 satir (mesru tur icin), etiketsiz bicimde 1 esleme noktasi`);
asama                    kayitta tur  acik kabul  izinli kabul  kapatilan tur  erisilen yan etkili
mevcut kod tabani                  9           9             4              5                5 / 0
uc yeni tur eklendi               12          12             4              8                7 / 0

yeni tur icin yazilan kod: acik kayitta 0 satir (tur tanimlandigi anda kabul edilir),
izin listesinde 1 satir (mesru tur icin), etiketsiz bicimde 1 esleme noktasi

Açık kabul sütunu kod tabanıyla birlikte büyüyor: dokuzdan on ikiye. Erişilen yan etkili tür beşten yediye çıkıyor ve bu artış için kimsenin bir şey yazması gerekmedi — üç tür tanımlandı, kabul kümesi kendiliğinden genişledi. Açık kayıtta kapsama, kod tabanının büyümesiyle ters yönde hareket eder.

İzin listesi sütunu her iki satırda da dört. Kapatılan tür beşten sekize çıkıyor: liste değişmediği hâlde kapsaması arttı, çünkü kapsama listenin uzunluğunda değil, listenin dışında bıraktığı kümededir. Yan etkili erişim iki satırda da sıfır.

Etiketsiz biçim bu tabloda hiç görünmüyor, çünkü orada kabul edilen tür kümesi diye bir büyüklük yoktur. Bedeli alt satırdadır: her yeni türün eşlemesi bir yerde elle yazılır. Üç biçim üç ayrı yerde iş yapar — biri hiçbir şey yapmaz ve her şeyi kabul eder, biri bir liste tutar, biri türü veriden tamamen çıkarır.

Üç eşleme noktası bugünkü sayıdır ve o da büyür. Etiketsiz biçimin bedeli sabit bir kerelik iş değil, ileti biçimi başına tekrarlanan bir iştir: dördüncü bir meşru ileti biçimi eklendiğinde dördüncü eşleme yazılır. Fark şudur ki bu iş unutulduğunda ileti çözülmez — eksiklik bir açık değil, kırık bir akıştır ve ilk çağrıda görülür. İzin listesinde ise unutulan bir satır kapsamayı sessizce daraltmaz, genişletmez de; kapsama listeye eklenmedikçe sabittir.

Özet

  • Ölçü birimi güvenilir tür kümesidir. Bir iletideki tür kararı tek değildir: altı ileti 11 etiket taşıyan düğüm içeriyor ve red kararı çoğu zaman derinlikteki bir düğümden geliyor.
  • Açık kayıt 9 türün 9’unu kabul etti, 11 düğümü kurdu ve 4’ü yan etkili türdü. İzin listesi kabul edilen türü 4’e, kurulan düğümü 5’e indirdi, yan etkili kurulumu 0’ladı ve 3 iletiyi reddetti.
  • Etiketsiz veri biçiminde yapı kurma noktası 0’dır ve bu bir savunmanın başarısı değil, kararın veriden çıkarılmasıdır. Bedeli 3 elle eşleme noktasıdır.
  • Kod tabanına 3 yeni tür eklendiğinde açık kabul 9’dan 12’ye, erişilen yan etkili tür 5’ten 7’ye çıktı ve bunun için hiçbir satır yazılmadı. İzin listesinin kabulü 4’te kaldı, kapattığı tür 5’ten 8’e çıktı.
  • Aynı izin listesi yalnız kök düğüme uygulandığında 5 ileti kabul edildi, 10 düğüm kuruldu ve 3 yan etkili kurulum oldu; her düğüme uygulandığında bu sayılar 3, 5 ve 0. Altı iletinin 4’ü birden çok düğümde etiket taşıyor.
  • Kapsama listenin uzunluğunda değil, listenin dışında bıraktığı kümededir: liste sabit kaldığı hâlde kod tabanı büyüdükçe kapsaması artar.

Sonraki Adım

Yedi ders boyunca tek bir sınır çizildi: dışarıdan gelen veri nereye giriyor, nereden çıkıyor, hangi metne katılıyor, hangi şablona ulaşıyor, hangi baytla saklanıyor, hangi adrese gidiyor ve hangi türü kuruyor. Her birinde kapsama sayıldı ve dışarıda kalan nokta yazıldı. Bütün bu ölçümlerin ortak varsayımı, sorunun verinin kendisinde olduğuydu.

Veri doğru olduğunda da bir soru kalır. Sözleşmeye tam uyan bir abone numarası, biçimi kusursuz bir fatura dönemi ve izin listesinden geçen bir tür — hepsi geçerliyken, isteği yapanın kim olduğu ve o kişinin bu kayda hakkı olup olmadığı hiç sorulmadı. Sonraki konu Kimlik, Oturum ve Yetki Hataları bu soruyu aynı ölçüyle ele alır: kimlik doğrulama gerçekleştiriminin sızdırdığı bilgi, oturumun yenilenmediği nokta ve kaç ucun nesne düzeyinde denetim yaptığı.

İ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