Ders 09 / 17
Oturum Yönetimi Hataları
Oturum kararlarının akış kümesi üzerinde sayılması: yedi akışın dördü oturumun yetki düzeyini yükseltir ve ikisi kimliği yenilemediği için sabitleme noktasıdır, üç geçersiz kılma akışının ikisi sunucu tarafında hiçbir kaydı silmediği için sekiz oturum yaşamaya devam eder, merkezî oturum kalıbı bu sayıyı ikiye indirir, sekizinci akış eklendiğinde dağıtık kurulumda dışarıda kalan akış beşe çıkar ve kalıbı hiç çağırmayan devir akışının yalnız yetki sürümüne bağlı bir kalıpla kapsandığı ölçülür.
İçindekiler
Önceki ders kimliğin belirlendiği anı ölçtü ve sekiz noktanın beşinin kullanıcı varlığını sızdırdığını saydı. Doğrulama başarılı olduğunda ise ortaya yeni bir nesne çıkar: sonraki isteklerde kimliği yeniden sormamak için üretilen oturum kimliği. Bu kimlik bir kez üretilir ve sonrasında pek çok akış tarafından değiştirilir, yükseltilir ya da bırakılır.
Oturumun ne olduğu ve nasıl saklandığı Kimlik ve Yetki kursunda kuruldu. Burada ölçülen şey kararların nerede verildiğidir. Oturumla ilgili her karar bir akışın içinde alınır; akış sayısı arttıkça karar sayısı artar ve her karar ayrı yazıldığında bazıları yanlış yazılır. Bu dersin sorusu şudur: kaç akış oturum kimliğine dokunuyor, kaçı doğru dokunuyor, kaçı dışarıda kalıyor.
İki Kural, Yedi Akış
Oturum kimliğine dokunan akışlar iki türe ayrılır. Yükseltme akışı oturumun yetki düzeyini artırır: anonim bir oturum girişli olur, girişli bir oturum ikinci etmeni tamamlanmış olur, bir görevli oturumu yönetici yetkisi kazanır. Bu türde kural tektir — düzey değiştiğinde oturum kimliği yenilenir. Yenilenmediğinde, düzey değişmeden önce bilinen bir kimlik, düzey değiştikten sonra da geçerli kalır; buna oturum sabitleme (session fixation) denir.
Geçersiz kılma akışı ise yetkiyi geri alır: çıkış, parola değişikliği, aboneliğin başka bir kişiye devri. Bu türde kural sunucu tarafındadır — silinecek şey defterdeki kayıttır. İstemci tarafında çerezin silinmesi defterle ilgili hiçbir şey yapmaz; kimliği elinde tutan bir istemci onu göndermeye devam edebilir.
Aşağıdaki akış kümesi ve oturum defteri bir modeldir: gerçek bir oturum deposu, gerçek bir çerez ve gerçek bir abone yoktur.
// oturum/model.mjs — abone portalinin oturum akislari ve sunucu tarafi oturum defteri (model): // gercek bir oturum deposu, gercek bir cerez ve gercek bir abone yok. // KH5: bir akis oturumun yetki duzeyini yukseltiyorsa oturum kimligi yenilenmelidir // (beklenen: "yenile"); yetkiyi geri aliyorsa etkilenen oturumlar sunucu tarafinda silinmelidir // (beklenen: cikis icin "tek", digerleri icin "hepsi"). `simdi` alani bugunku davranistir. export const AKIS = [ { ad: "portal-giris", tur: "yukselt", simdi: "yenile" }, { ad: "ikinci-etmen-onayi", tur: "yukselt", simdi: "yerinde" }, { ad: "rol-yukseltme", tur: "yukselt", simdi: "yerinde" }, { ad: "saha-uygulamasi-eslesme", tur: "yukselt", simdi: "yenile" }, { ad: "cikis", tur: "gecersiz", simdi: "tek" }, { ad: "parola-degistirme", tur: "gecersiz", simdi: "hicbiri" }, { ad: "abone-devri", tur: "gecersiz", simdi: "hicbiri" }, ]; export const beklenen = (a) => a.tur === "yukselt" ? "yenile" : a.ad === "cikis" ? "tek" : "hepsi"; // KH6: oturum defteri sunucu tarafindadir; bir kimlik ancak defterde kaydi varken cozulur. // Istemci tarafinda cerezin silinmesi deftere dokunmaz. `surumlu` acikken kayit abonenin // yetki surumunu tasir ve surum degistiginde silinmeden gecersizlesir. export function defterAc({ surumlu = false } = {}) { const kayit = new Map(), surum = new Map(); let sayac = 0; const s = (abone) => surum.get(abone) ?? 1; return { ac(abone, duzey) { const k = `o-${++sayac}`; kayit.set(k, { abone, duzey, surum: s(abone) }); return k; }, coz(k) { const v = kayit.get(k); if (!v) return null; return surumlu && v.surum !== s(v.abone) ? null : v; }, yerinde(k, duzey) { const v = kayit.get(k); if (v) v.duzey = duzey; }, sil: (k) => kayit.delete(k), silHepsi(abone) { let n = 0; for (const [k, v] of kayit) if (v.abone === abone) { kayit.delete(k); n++; } return n; }, surumArtir(abone) { surum.set(abone, s(abone) + 1); }, canli(abone) { return [...kayit.keys()].filter((k) => this.coz(k)?.abone === abone).length; }, }; }
// oturum/olcum.mjs — kac akis oturum kimligi uretiyor, kaci yenilemiyor, gecersiz kilma // yoksa kac oturum yasamaya devam ediyor. import { AKIS, beklenen, defterAc } from "./model.mjs"; const yaz = (g, ...s) => console.log(s.map((v, i) => (g[i] < 0 ? String(v).padEnd(-g[i]) : String(v).padStart(g[i]))).join("")); // KH7: sabitleme noktasi, yukseltme akisindan sonra eski kimligin hala yukseltilmis duzeyi // cozdugu akistir. Asagidaki dongu bunu defter uzerinde gercekten kosturur. const yukseltme = AKIS.filter((a) => a.tur === "yukselt"); const A = [-24, 10, 22, 20]; console.log(`1. ${yukseltme.length} yukseltme akisi: eski kimlik giristen sonra ne cozuyor`); yaz(A, "akis", "simdi", "eski kimligin duzeyi", "sabitleme noktasi"); let sabitleme = 0; for (const a of yukseltme) { const d = defterAc(); const eski = d.ac("a-4471", "anonim"); if (a.simdi === "yenile") { d.sil(eski); d.ac("a-4471", "girisli"); } else d.yerinde(eski, "girisli"); const duzey = d.coz(eski)?.duzey ?? "cozulmuyor"; if (duzey === "girisli") sabitleme++; yaz(A, a.ad, a.simdi, duzey, duzey === "girisli" ? "EVET" : "hayir"); } console.log(`oturum kimligi ureten akis: ${yukseltme.length}, yenilemeyen: ${sabitleme}`); // KH8: her abonenin uc ayri uctan acilmis uc canli oturumu vardir; olculen sey bir gecersiz // kilma akisindan sonra defterde kalan oturum sayisidir. const uygula = (siyaset, d, k) => siyaset === "tek" ? d.sil(k) : siyaset === "hepsi" ? d.silHepsi("a-4471") : 0; const yasayan = (siyaset) => { const d = defterAc(); const kimlikler = [d.ac("a-4471", "girisli"), d.ac("a-4471", "girisli"), d.ac("a-4471", "girisli")]; uygula(siyaset, d, kimlikler[0]); return d.canli("a-4471"); }; const gecersiz = AKIS.filter((a) => a.tur === "gecersiz"); const B = [-22, 12, 10, 12, 10]; console.log("\n2. gecersiz kilma akislari (baslangic: her akista 3 canli oturum)"); yaz(B, "akis", "simdi", "yasayan", "beklenen", "yasayan"); let simdiToplam = 0, kalipToplam = 0; for (const a of gecersiz) { const s = yasayan(a.simdi), b = yasayan(beklenen(a)); simdiToplam += s; kalipToplam += b; yaz(B, a.ad, a.simdi, s, beklenen(a), b); } yaz(B, "toplam", "", simdiToplam, "", kalipToplam); // 3. kapsama: kac akis kalibin bekledigi davranisi zaten yapiyor const kapsanan = (kume) => kume.filter((a) => a.simdi === beklenen(a)).length; const YENI = { ad: "saha-cihaz-degisimi", tur: "yukselt", simdi: "yerinde" }; const genis = [...AKIS, YENI]; const C = [-34, 6, 10, 10, 12]; console.log("\n3. akis kumesi kapsamasi"); yaz(C, "kume", "akis", "kapsanan", "disarida", "disarida %"); for (const [ad, k, merkezi] of [ ["bugunku dagitik durum", AKIS, false], ["merkezi oturum kalibi", AKIS, true], ["yeni akis eklendi, dagitik", genis, false], ["yeni akis eklendi, merkezi kalip", genis, true], ]) { const kap = merkezi ? k.length : kapsanan(k); yaz(C, ad, k.length, kap, k.length - kap, (((k.length - kap) / k.length) * 100).toFixed(1)); } // 4. kalibin ulasamadigi akis: `abone-devri` abonelik kaydini portalin disindan degistirir ve // oturum kalibini cagirmayabilir. Surume bagli kalipta cagri gerekmez. const devir = (surumlu, bildirir) => { const d = defterAc({ surumlu }); d.ac("a-4471", "girisli"); d.ac("a-4471", "girisli"); d.ac("a-4471", "girisli"); if (bildirir) d.silHepsi("a-4471"); d.surumArtir("a-4471"); // abonelik kaydi her durumda degisir return d.canli("a-4471"); }; const D = [-24, 16, 16, 16]; console.log("\n4. abone-devri akisi (3 canli oturum): kalip cagrilmazsa ne oluyor"); yaz(D, "kurulum", "olay bildirdi", "yasayan oturum", "kapsanan akis"); for (const [ad, surumlu, bildirir] of [ ["cagriya bagli kalip", false, true], ["cagriya bagli kalip", false, false], ["surume bagli kalip", true, false], ]) yaz(D, ad, bildirir ? "evet" : "hayir", devir(surumlu, bildirir), `${surumlu || bildirir ? gecersiz.length : gecersiz.length - 1} / ${gecersiz.length}`);
1. 4 yukseltme akisi: eski kimlik giristen sonra ne cozuyor akis simdi eski kimligin duzeyi sabitleme noktasi portal-giris yenile cozulmuyor hayir ikinci-etmen-onayi yerinde girisli EVET rol-yukseltme yerinde girisli EVET saha-uygulamasi-eslesme yenile cozulmuyor hayir oturum kimligi ureten akis: 4, yenilemeyen: 2 2. gecersiz kilma akislari (baslangic: her akista 3 canli oturum) akis simdi yasayan beklenen yasayan cikis tek 2 tek 2 parola-degistirme hicbiri 3 hepsi 0 abone-devri hicbiri 3 hepsi 0 toplam 8 2 3. akis kumesi kapsamasi kume akis kapsanan disarida disarida % bugunku dagitik durum 7 3 4 57.1 merkezi oturum kalibi 7 7 0 0.0 yeni akis eklendi, dagitik 8 3 5 62.5 yeni akis eklendi, merkezi kalip 8 8 0 0.0 4. abone-devri akisi (3 canli oturum): kalip cagrilmazsa ne oluyor kurulum olay bildirdi yasayan oturum kapsanan akis cagriya bagli kalip evet 0 3 / 3 cagriya bagli kalip hayir 3 2 / 3 surume bagli kalip hayir 0 3 / 3
Sayılar ölçüm sınıfındadır; girdileri KH5–KH8’dir.
Dört Akış Üretiyor, İkisi Yenilemiyor
Birinci tablo yükseltme tarafını sayar. Dört akış oturumun yetki düzeyini değiştiriyor;
ikisi kimliği yeniliyor, ikisi kaydı yerinde yükseltiyor. Yerinde yükselten iki akışta
eski kimlik girisli düzeyini çözmeye devam ediyor; yenileyen iki akışta cozulmuyor.
İki sabitleme noktasının hangileri olduğu öğreticidir. portal-giris doğru yazılmıştır —
bu akış herkesin baktığı akıştır ve kural en çok orada bilinir. Yanlış yazılan ikisi
ikinci-etmen-onayi ve rol-yukseltme’dir: ikisi de girişten sonra gelen ve girişin
kendisi sanılmayan akışlardır. Kural “girişte kimlik yenilenir” biçiminde ezberlendiğinde,
düzeyi girişten sonra artıran akışlar kuralın dışında kalır.
Sayının anlamı da buradadır. “Oturum sabitlemeye karşı korunuyoruz” cümlesi tek bir akış hakkında doğru, dört akış hakkında yarı doğrudur. Ölçülebilir biçimi şudur: oturum kimliği üreten akış 4, yenilemeyen 2.
Yerinde yükseltmenin niçin yazıldığını görmek de kolaydır. Kaydı yerinde güncellemek daha az iş yapar: yeni bir kimlik üretilmez, istemciye yeni bir değer gönderilmez, aradaki istekler etkilenmez. Yenileme ise kaydın kopyalanmasını, eskisinin silinmesini ve istemcinin yeni değeri almasını gerektirir. Sabitleme noktalarının ikisinin de bu ucuz yolu seçmiş olması bir dikkatsizlik değil, kuralın o akışta hiç sorulmamış olmasıdır.
Sekiz Oturum Yaşamaya Devam Ediyor
İkinci tablo geçersiz kılma tarafını sayar. Her akış üç canlı oturumu olan bir abone üzerinde koşuyor; ölçülen şey akıştan sonra defterde kalan kayıt sayısıdır.
cikis akışı kendi oturumunu siliyor ve geriye iki oturum kalıyor. Bu doğrudur: bir uçtan
çıkmak öteki uçlardaki oturumları kapatmaz. parola-degistirme ve abone-devri ise
defterde hiçbir kaydı silmiyor; üçü de yaşamaya devam ediyor. Toplam sekiz oturum, yetkinin
geri alındığı varsayılan üç akıştan sonra hâlâ canlı.
İki akışın niçin bu durumda olduğu, işin nerede yapıldığıyla ilgilidir. Parola değiştirme akışının görünür işi kimlik bilgisini güncellemektir; oturum defterine dokunmak o işin parçası gibi görünmez. Abone devri ise portalın değil, aboneliği yöneten tarafın akışıdır ve oturum defterinin varlığından haberdar olmayabilir. Her iki durumda da eksik olan şey bir denetim değil, bir çağrıdır.
Sayının nasıl ölçüldüğü de önemlidir. Her akış üç canlı oturumu olan bir abone üzerinde koşuyor; üç oturum aynı kişinin ayrı uçlarıdır. Abone başına oturum sayısı arttıkça, geçersiz kılmayan bir akışın bıraktığı oturum sayısı doğrudan artar. Bu yüzden ölçünün birimi “akış” değil, akıştan sonra yaşayan oturumtur: iki eksik akış, oturum sayısıyla çarpılarak büyüyen bir kalemdir.
Beklenen sütun aynı akışları doğru siyasetle koşuyor: cikis için tek, diğer ikisi için
hepsi. Toplam sekizden ikiye iniyor ve kalan iki oturum çıkış akışının öteki
uçlarındaki oturumlardır — kapatılmaması gereken oturumlar. Ölçüm burada bir şeyi daha
gösteriyor: doğru sayı sıfır değildir. “Hepsini kapat” her akış için doğru olsaydı,
kullanıcı bir uçtan çıktığında öteki uçlardan da düşerdi.
Kalıbın Kapsadığı Akış
Üçüncü tablo kapsamayı sayar. Yedi akışın üçü kalıbın beklediği davranışı yapıyor; dördü dışarıda — dışarıda kalan oran %57,1.
Merkezî oturum kalıbı tek bir işlevdir ve iki şeyi bir yerde tutar: hangi olayın hangi siyasete karşılık geldiği ve o siyasetin defter üzerinde nasıl uygulandığı. Akışlar kararı kendileri vermez, olayı bildirir. Bu kurulumda kapsanan akış sayısı 7’dir ve dışarıda kalan sıfırdır — ama bu sıfır, kalıbın akıllı olmasından değil, kararın bir kez verilmiş olmasından gelir.
Son iki satır yeni akış sorusunu yanıtlar. Sekizinci akış — saha-cihaz-degisimi, bir saha
görevlisinin uçbirimini değiştirmesi — dağıtık kurulumda dışarıda kalanı 4’ten 5’e çıkarır
ve oran %62,5’e yükselir. Merkezî kalıpta aynı akış bir olay bildirir ve dışarıda kalan
sıfır kalır. Dağıtık kurulumda her yeni akış, iki kuralın ikisini de yeniden hatırlamayı
gerektirir; kalıpta yalnız olayın adını doğru yazmayı.
Kalıbı Çağırmayan Akış
Dördüncü tablo, üçüncü tablodaki sıfırın hangi varsayıma dayandığını açar. Merkezî kalıp bir çağrı kalıbıdır: akış olayı bildirdiğinde çalışır, bildirmediğinde çalışmaz. Bu varsayım yedi akışın altısı için sorunsuzdur, çünkü altısı portalın kendi kodudur.
abone-devri farklıdır. Abonelik kaydını değiştiren taraf portal değildir; devir, abonelik
kaydını doğrudan güncelleyen ayrı bir akıştır. Tablo iki durumu ayırıyor: akış olayı
bildirdiğinde üç oturum sıfıra iniyor, bildirmediğinde üçü de yaşamaya devam ediyor ve
kapsanan akış 3’ten 2’ye düşüyor. Merkezî kalıp yerinde duruyor, doğru yazılmış ve
çalışıyor; kapsamadığı tek şey onu çağırmayan akış.
Son satır kalıbın biçimini değiştiriyor. Oturum kaydı abonenin yetki sürümünü taşırsa, çözümleme her istekte kayıttaki sürümü abonenin güncel sürümüyle karşılaştırır. Devir akışı abonelik kaydını değiştirdiğinde sürüm zaten artar; oturum kalıbını hiç çağırmamasına karşın üç oturum da geçersizleşir — yaşayan oturum 0, kapsanan akış 3/3. Fark, savunmanın nereye bağlandığıdır: çağrıya bağlı kalıp akışın hatırlamasını bekler, veriye bağlı kalıp akışın zaten yaptığı değişikliği ölçüt olarak kullanır. İkincisinin kapsaması, akış kümesi büyüdükçe sabit kalır.
Özet
- Oturum kararları iki kurala indirgenir: yetki düzeyi yükselten akış kimliği yeniler, yetki geri alan akış sunucu tarafındaki kaydı siler. İkisi de akış başına ayrı verilir.
- Yedi akışın dördü oturum kimliği üretiyor; ikisi kimliği yenilemediği için sabitleme noktasıdır ve yanlış yazılan ikisi girişten sonra gelen akışlardır.
- Üç geçersiz kılma akışının ikisi defterde hiçbir kaydı silmiyor; üçünden sonra sekiz oturum yaşamaya devam ediyor, doğru siyasetle bu sayı ikiye iniyor.
- Doğru sayı sıfır değildir: çıkış akışından sonra öteki uçlardaki iki oturumun yaşaması beklenen sonuçtur; siyaset akış başına ayrılır.
- Yedi akışın üçü kalıbın beklediğini yapıyor, dördü dışarıda kalıyor (%57,1); merkezî kalıpta sekizinci akış eklendiğinde dışarıda kalan sıfır kalırken dağıtık kurulumda beşe çıkıyor.
- Çağrıya bağlı kalıp, kendisini çağırmayan akışı kapsamaz: devir akışı olayı bildirmediğinde üç oturum da yaşıyor. Oturum kaydı abonenin yetki sürümünü taşıdığında aynı akış hiçbir çağrı yapmadan üç oturumu geçersiz kılıyor.
Sonraki Adım
Bu iki ders isteğin arkasındaki kimliği güvence altına aldı: kim olduğu sızdırılmadan belirlendi ve o kimliğin taşıyıcısı doğru anlarda yenilendi, doğru anlarda silindi. Kimliğin doğru bilinmesi ise kararın yalnız yarısıdır. Bilinen bir kimlik bir uca istek yolladığında sorulması gereken ikinci soru vardır ve bu soru uç başına sorulur: bu kimlik bu nesneye dokunabilir mi? Sonraki ders yetki matrisini çıkarır ve konunun en önemli sayısını verir: kaç uç yalnız rol denetimi yapıyor, kaçı istenen nesnenin sahibini de denetliyor, yeni bir uç eklendiğinde merkezî kalıp onu kapsıyor mu.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.