Ders 02 / 12
Dinamik Uygulama Güvenlik Testi
96 uçluk bir yüzeyde çalışan uygulamayı tarayan aracın erişebildiği ve erişemediği alan: anonim gezinme 13 uca varıp 2 açık buluyor, oturum ve kayıtlı akış eklendiğinde 84 uca ve 10 açığa çıkıyor, 12 yönetim ucu her yapılandırmada dışarıda kalıyor. 28 açığın 18'i görülmüyor; 5'i erişilemediği için, 13'ü erişildiği halde. Aynı tarama hatta üç ayrı yere konuyor: her yapıda tam 5 316 dakika ve sıfır gecikme, gecelik tam 443 dakika ve 6 yapı gecikme, yalnız değişen uç 126 dakika ama 3 açığı hiç görmüyor.
İçindekiler
Bir önceki ders statik çözümlemenin görüş alanını saydı ve 163 açık noktanın 62’sinin her eşikte dışarıda kaldığını gösterdi. Bunların büyük bölümü kararı çalışma zamanında verilen noktalardı: bir isteğin çağıranın yetkisine uyup uymadığı, bir başlığın gerçekten gönderilip gönderilmediği kaynak metninden okunmuyordu. Bu noktalara ulaşmanın bir yolu var — uygulamayı ayağa kaldırıp dışarıdan istemek.
Dinamik uygulama güvenlik testi (dynamic application security testing), çalışan bir uygulamaya istek göndererek açık arayan denetimdir. Kaynak koda bakmaz, dolayısıyla dilden ve çerçeveden bağımsızdır; buna karşılık yalnız erişebildiğini görür. Bu ders o sınırı sayar: kaç uca varılıyor, kaç uca varılamıyor, varılan uçta ne kaçıyor ve bütün bunlar hattan kaç dakika alıyor.
- OD7. Yüzey, abone portalı ile sayaç alım servisinin 96 ucudur. Envanter kurgudur ve bir modeldir; bu sayfada hiçbir hedefe istek gönderilmez.
- OD8. Her ucun iki bağımsız özelliği vardır. Keşif, adresin nasıl bulunduğudur: sayfa bağlantısı, yalnız istemci betiğinin kurduğu adres ya da bir arayüz bildirimi. Erişim, uçtan geçerli yanıt alma koşuludur: anonim, oturum gerektiren, yalnız belirli bir akış sırasında ortaya çıkan, yönetim rolü isteyen.
- OD9. Bir yapılandırma, tarayıcıya verilen yetenek kümesidir. Üçü karşılaştırılır ve her biri bir öncekinin üstüne tek bir yetenek ekler.
- OD10. Her açığın bir görülür değeri vardır: araç o uca ulaştığında açığı yakalar mı. Değer ucun kendi özelliğidir, yapılandırmadan bağımsızdır; böylece yetenek eklemek bulunan açığı yalnız erişim üzerinden değiştirir.
- OD11. İstek başına 0,05 model dakikası harcanır. Süreler veridir, bu sayfada yapılmış bir ölçüm değildir.
- OD12. Bir kez incelenip yanlış pozitif olarak işaretlenen bulgu bastırma listesine girer ve sonraki koşumlarda yapıyı bir daha durdurmaz.
Varılan ve Varılamayan Yüzey
// yuzey.mjs — calisan uygulamayi tarayan aracin erisebildigi ve erisemedigi yuzey. // MODELDIR; uc envanteri kurgudur, hicbir hedefe istek gonderilmez. export const TOHUM = 20260317, UC = 96, ISTEK_DK = 0.05; const uretec = (t) => { let s = t >>> 0; return () => (s = (s * 1664525 + 1013904223) >>> 0) / 4294967296; }; const r = uretec(TOHUM); // kesif: uc adresinin nasil bulundugu. erisim: uca gecerli yanit aldirmanin kosulu. const KESIF = [["baglanti", 0.52], ["istemci", 0.27], ["bildirim", 0.21]]; const ERISIM = [["anonim", 0.30], ["oturum", 0.42], ["sira", 0.16], ["yonetim", 0.12]]; // Acik sinifi: gorme = arac o sinifi erisebildigi bir ucta yakalama olasiligi. export const SINIF = [ { ad: "yapilandirma basligi", pay: 0.22, gorme: 0.95, istek: 4 }, { ad: "yansiyan girdi", pay: 0.26, gorme: 0.85, istek: 26 }, { ad: "hata mesaji sizintisi", pay: 0.18, gorme: 0.75, istek: 12 }, { ad: "erisim denetimi", pay: 0.20, gorme: 0.15, istek: 18 }, { ad: "is akisi kurali", pay: 0.14, gorme: 0.05, istek: 22 }, ]; const sec = (tablo, u) => { let t = 0; for (const [ad, p] of tablo) { t += p; if (u < t) return ad; } return tablo.at(-1)[0]; }; const sinifSec = (u) => { let t = 0; for (const s of SINIF) { t += s.pay; if (u < t) return s; } return SINIF.at(-1); }; export const UCLAR = Array.from({ length: UC }, (_, i) => { const kesif = sec(KESIF, r()), erisim = sec(ERISIM, r()); const acik = r() < 0.34 ? sinifSec(r()) : null; // gorulur, ucun kendi ozelligidir: arac uca ulassa bile bu acigi yakalar mi. const gorulur = acik ? r() < acik.gorme : false; return { i, kesif, erisim, acik, gorulur, istek: 6 + (acik ? acik.istek : 14), yanlis: r() < 0.13 }; }); // Uc yapilandirma: her biri bir onceki yetenegin ustune bir tane ekler. export const YAPILANDIRMA = [ { ad: "anonim gezinme", kesif: ["baglanti"], erisim: ["anonim"] }, { ad: "oturumlu gezinme", kesif: ["baglanti", "istemci"], erisim: ["anonim", "oturum"] }, { ad: "oturum + kayitli akis", kesif: ["baglanti", "istemci", "bildirim"], erisim: ["anonim", "oturum", "sira"] }, ]; export const erisilen = (y) => UCLAR.filter((u) => y.kesif.includes(u.kesif) && y.erisim.includes(u.erisim)); export function kosum(y) { const varilan = erisilen(y); const istek = varilan.reduce((a, u) => a + u.istek, 0); const gercek = varilan.filter((u) => u.acik && u.gorulur); const yanlisPoz = varilan.filter((u) => u.yanlis); const acikUc = UCLAR.filter((u) => u.acik); return { ad: y.ad, varilan, istek, gercek, yanlisPoz, erisilemeyen: acikUc.filter((u) => !varilan.includes(u)).length, gorulmeyen: acikUc.length - gercek.length, dk: Math.round(istek * ISTEK_DK) }; } const gen = (B) => (h) => h.map((v, j) => (j ? String(v).padStart(B[j]) : String(v).padEnd(B[0]))).join(""); if (import.meta.url === `file://${process.argv[1]}`) { const acikUc = UCLAR.filter((u) => u.acik); console.log(`model (tohum ${TOHUM}): ${UC} uc, ${acikUc.length} tanesinde gercek acik; ` + `${UCLAR.filter((u) => u.erisim === "yonetim").length} uc yonetim rolu istiyor`); const y1 = gen([23, 9, 8, 8, 9, 9, 12, 14, 11]); console.log("\n" + y1(["yapilandirma", "erisilen", "istek", "bulgu", "gercek", "yanlis+", "gorulmeyen", "erisilemeyen", "tarama dk"])); const sonuc = YAPILANDIRMA.map(kosum); for (const o of sonuc) { console.log(y1([o.ad, o.varilan.length, o.istek, o.gercek.length + o.yanlisPoz.length, o.gercek.length, o.yanlisPoz.length, o.gorulmeyen, o.erisilemeyen, o.dk])); } const tam = sonuc.at(-1); const y2 = gen([23, 7, 11, 10, 14, 16]); console.log("\nen genis yapilandirmada acik sinifina gore"); console.log(y2(["acik sinifi", "acik", "erisilen", "bulunan", "erisilemeyen", "erisilip kacan"])); for (const s of SINIF) { const l = acikUc.filter((u) => u.acik.ad === s.ad); const e = l.filter((u) => tam.varilan.includes(u)); const b = tam.gercek.filter((u) => u.acik.ad === s.ad).length; console.log(y2([s.ad, l.length, e.length, b, l.length - e.length, e.length - b])); } console.log(`\nyetenek eklemenin karsiligi: erisilen uc ${sonuc[0].varilan.length} -> ` + `${sonuc[1].varilan.length} -> ${tam.varilan.length}, bulunan gercek acik ` + `${sonuc[0].gercek.length} -> ${sonuc[1].gercek.length} -> ${tam.gercek.length}, ` + `tarama ${sonuc[0].dk} -> ${sonuc[1].dk} -> ${tam.dk} dakika`); }
model (tohum 20260317): 96 uc, 28 tanesinde gercek acik; 12 uc yonetim rolu istiyor yapilandirma erisilen istek bulgu gercek yanlis+ gorulmeyen erisilemeyen tarama dk anonim gezinme 13 278 2 2 0 26 25 14 oturumlu gezinme 50 1066 9 5 4 23 19 53 oturum + kayitli akis 84 1772 17 10 7 18 5 89 en genis yapilandirmada acik sinifina gore acik sinifi acik erisilen bulunan erisilemeyen erisilip kacan yapilandirma basligi 6 3 3 3 0 yansiyan girdi 5 5 5 0 0 hata mesaji sizintisi 3 3 2 0 1 erisim denetimi 7 7 0 0 7 is akisi kurali 7 5 0 2 5 yetenek eklemenin karsiligi: erisilen uc 13 -> 50 -> 84, bulunan gercek acik 2 -> 5 -> 10, tarama 14 -> 53 -> 89 dakika
İlk satır bir taramanın en yaygın kurulumudur: uygulamanın adresini ver ve gezmesini bekle. 96 ucun 13’üne varıyor. 28 açığın 25’i o anda erişilemeyen bir uçtadır ve rapor iki gerçek bulguyla kapanır. Bu rapor yanlış değildir; eksik olduğu hiçbir yerinde yazmaz. “İki bulgu” sonucunu okuyan biri yüzeyin yüzde 86’sının hiç denenmediğini bilmez.
İkinci satır tarayıcıya geçerli bir oturum ve istemci tarafında kurulan adresleri veriyor: erişilen uç 50’ye, gerçek bulgu 5’e çıkıyor. Üçüncü satır arayüz bildirimini ve kaydedilmiş akış sırasını ekliyor — böylece yalnız belirli bir sırayla ulaşılabilen 16 uca da varılıyor. Erişilen uç 84, gerçek bulgu 10. Bulunan açık sayısını üçe katlayan şey aracın yeteneği değil, ona verilen erişimdir. 12 yönetim ucu hiçbir satırda taranmıyor, çünkü modelde tarayıcıya yönetim kimliği verilmemiştir; bu, kapsam kararının doğrudan yanlış negatife dönüştüğü yerdir.
İkinci tablo görülmeyen 18 açığı iki gerekçeye ayırıyor ve ayrım karar taşıyor. Beşi erişilemediği için görülmüyor — üç yapılandırma başlığı ve iki akış kuralı, yönetim ucundadır. Bunlar kapsamı genişleterek kapanabilir. On üçü erişildiği halde görülmüyor; kapsam genişletmek bunlara hiçbir şey yapmaz. Erişim denetimi sınıfının yedi açığının yedisine de ulaşılıyor ve hiçbiri bulunmuyor: bir isteğin yetkisiz olduğunu anlamak, aynı isteği iki farklı kimlikle gönderip yanıtları karşılaştırmayı gerektirir; tek kimlikli bir gezinme bunu yapamaz. İş akışı kuralı sınıfı da benzerdir — bir adımın atlanabildiğini görmek için beklenen sıranın ne olduğunu bilmek gerekir, ve tarayıcı bunu bilmez.
Bir önceki dersle birlikte okununca tablo tamamlanıyor. Statik çözümleme yapılandırma başlığını hiç görmüyordu; buradaki araç altısının üçünü buluyor. Buna karşılık erişim denetimi sınıfı her iki denetimde de sıfır bulguyla kapanıyor. İki denetimin birleşimi bile bir sınıfı boş bırakıyor, ve o sınıfın hangisi olduğu ancak kapsam listesi yazıldığında görünür.
Hattaki Yerleşim
Tarama 89 model dakikası sürüyor. Bu süre hattaki yeri belirler ve yer, bulgunun geliştiriciye kaç yapı sonra döndüğünü belirler.
// yerlesim.mjs — ayni tarama hatta uc ayri yere konuyor: her yapida tam, her yapida // yalniz degisen uc, gecelik tam. Sureler MODEL dakikasidir, olcum degildir. import { YAPILANDIRMA, erisilen, kosum, ISTEK_DK, TOHUM } from "./yuzey.mjs"; const uretec = (t) => { let s = t >>> 0; return () => (s = (s * 1664525 + 1013904223) >>> 0) / 4294967296; }; const r = uretec(TOHUM + 11); const YAPI = 60, GECE = 12; // gecelik kosum her 12 yapida bir const ULASILAN = erisilen(YAPILANDIRMA.at(-1)); const TAM_DK = kosum(YAPILANDIRMA.at(-1)).dk; // Her yapi bir kac uca dokunur. Bir acik bir yapida dogar; dogduran yapinin o uca // dokunmus olmasi sart degildir, cunku ortak kitaplik degisikligi baska uca yansir. const dokunan = Array.from({ length: YAPI }, () => new Set( Array.from({ length: 1 + Math.floor(r() * 3) }, () => ULASILAN[Math.floor(r() * ULASILAN.length)]))); const DOGAN = ULASILAN.filter((u) => u.acik && u.gorulur).map((u) => { const y = Math.floor(r() * YAPI); if (r() < 0.65) dokunan[y].add(u); // degisiklik dogrudan o uca dokunuyor return { uc: u, dogus: y }; }); // Bir yerlesim, hangi yapida hangi uc kumesinin tarandigini soyler. const YERLESIM = [ { ad: "her yapida tam", kapi: true, tara: (y) => ULASILAN, kosar: () => true }, { ad: "yalniz degisen uc", kapi: true, tara: (y) => [...dokunan[y]], kosar: () => true }, { ad: "gecelik tam", kapi: false, tara: (y) => ULASILAN, kosar: (y) => y % GECE === GECE - 1 }, ]; function olc(z) { const gorulen = new Set(), gecikme = [], blokeYapi = new Set(), yanlisYapi = new Set(); let dk = 0; for (let y = 0; y < YAPI; y++) { if (!z.kosar(y)) continue; const kume = z.tara(y); dk += kume.reduce((a, u) => a + u.istek, 0) * ISTEK_DK; for (const u of kume) { if (u.yanlis && !gorulen.has(u)) { gorulen.add(u); yanlisYapi.add(y); } const d = DOGAN.find((x) => x.uc === u && x.dogus <= y && !gorulen.has(x)); if (d) { gorulen.add(d); gecikme.push(y - d.dogus); blokeYapi.add(y); } } } const bulunan = gecikme.length; return { ad: z.ad, dk: Math.round(dk), bulunan, kacan: DOGAN.length - bulunan, ort: bulunan ? (gecikme.reduce((a, b) => a + b, 0) / bulunan).toFixed(1) : "-", enKotu: bulunan ? Math.max(...gecikme) : "-", bloke: z.kapi ? blokeYapi.size : 0, yanlisBloke: z.kapi ? [...yanlisYapi].filter((y) => !blokeYapi.has(y)).length : 0 }; } const gen = (B) => (h) => h.map((v, j) => (j ? String(v).padStart(B[j]) : String(v).padEnd(B[0]))).join(""); const y = gen([20, 11, 10, 9, 13, 13, 8, 14]); console.log(`${YAPI} yapi, tam tarama ${TAM_DK} model dakikasi, ` + `hattaki gorunur acik ${DOGAN.length}; gecikme yapi sayisidir`); console.log(y(["yerlesim", "toplam dk", "bulunan", "kacan", "ort. gecikme", "en kotu gec.", "bloke", "yanlis bloke"])); for (const z of YERLESIM) { const o = olc(z); console.log(y([o.ad, o.dk, o.bulunan, o.kacan, o.ort, o.enKotu, o.bloke, o.yanlisBloke])); } const [a, b, c] = YERLESIM.map(olc); console.log(`\ntam tarama her yapida ${a.dk} dakika alip ${a.bulunan} acigi ayni yapida ` + `veriyor; gecelik kosum ${c.dk} dakikaya, yani ${(100 * (1 - c.dk / a.dk)).toFixed(1)}% ` + `daha aza iniyor ve ayni ${c.bulunan} acigi ortalama ${c.ort} yapi sonra veriyor`); console.log(`degisen uc taramasi ${b.dk} dakikada ${b.bulunan} acik buluyor; ` + `${b.kacan} acik hic taranmayan bir ucta durdugu icin bulunmuyor`);
60 yapi, tam tarama 89 model dakikasi, hattaki gorunur acik 10; gecikme yapi sayisidir yerlesim toplam dk bulunan kacan ort. gecikme en kotu gec. bloke yanlis bloke her yapida tam 5316 10 0 0.0 0 10 0 yalniz degisen uc 126 7 3 2.7 19 7 6 gecelik tam 443 10 0 6.0 11 0 0 tam tarama her yapida 5316 dakika alip 10 acigi ayni yapida veriyor; gecelik kosum 443 dakikaya, yani 91.7% daha aza iniyor ve ayni 10 acigi ortalama 6.0 yapi sonra veriyor degisen uc taramasi 126 dakikada 7 acik buluyor; 3 acik hic taranmayan bir ucta durdugu icin bulunmuyor
Üç satır aynı taramayı, aynı yüzeyi ve aynı on açığı ele alıyor; ayrıldıkları yer bedeldir. Her yapıda tam tarama on açığın onunu da doğduğu yapıda veriyor — gecikme sıfır — ve karşılığında 5 316 model dakikası alıyor. Gecelik koşum aynı on açığı aynı eksiksizlikle buluyor, hattan yüzde 91,7 daha az dakika alıyor ve hiçbir yapıyı durdurmuyor. Ödediği şey tek bir sütunda: ortalama 6 yapı, en kötü durumda 11 yapı gecikme. Bir açık doğduktan sonra on bir yapı boyunca üretime gidebilir.
Üçüncü satır ucuzdur ve eksiktir. Yalnız değişen uçları taramak 126 dakikada kalıyor ama on açığın üçünü hiç bulmuyor. Nedeni ikinci kod bloğunun yorumunda yazılı: bir açık her zaman kendi ucuna dokunan değişiklikte doğmaz. Ortak kitaplıkta yapılan bir değişiklik başka bir ucun davranışını bozar, o uç altmış yapı boyunca bir daha değişmez ve değişen uç taraması oraya hiç uğramaz. Değişen dosyaya bakan bir denetim, değişmeyen yerde doğan açığı göremez.
Son sütun beklenmedik yerdedir. En ucuz yerleşim, yanlış pozitif yüzünden en çok yapıyı durduran yerleşimdir: 6 yapı. Tam tarama yedi yanlış pozitifin hepsini ilk koşumda çıkarır, hepsi bir kerede elenir ve bastırma listesine girer; değişen uç taraması aynı yedi bulguyu haftalara yayarak birer birer çıkarır ve her biri ayrı bir yapıyı durdurur. Toplam yanlış pozitif sayısı aynıdır, hatta bıraktığı iz farklıdır.
Özet
- Çalışan uygulamayı tarayan araç yalnız erişebildiğini görür: anonim gezinme 96 ucun 13’üne varıp 2 açık bulur, oturum ve kayıtlı akış eklendiğinde 84 uca ve 10 açığa çıkar.
- Görülmeyen 18 açık iki gerekçeye ayrılır ve ayrım karar taşır: 5’i erişilemediği için (kapsam genişletilerek kapanır), 13’ü erişildiği halde (kapsam genişletmek etkilemez).
- Erişim denetimi sınıfının yedi açığının yedisine de ulaşılır ve hiçbiri bulunmaz; aynı sınıf statik çözümlemede de sıfır bulguyla kapanmıştı — iki denetimin birleşimi bile onu boş bırakır.
- Hattaki yerleşim bulunan açığı değil gecikmeyi ve bedeli değiştirir: her yapıda tam tarama 5 316 dakika ve sıfır gecikme, gecelik koşum 443 dakika ve ortalama 6 yapı gecikme.
- Yalnız değişen uçları taramak 126 dakikaya iner ama on açığın üçünü hiç görmez, çünkü ortak bileşen değişikliğiyle doğan açık hiç dokunulmayan bir uçta durur.
- Yanlış pozitifin bedeli yerleşime bağlıdır: aynı yedi bulgu tam taramada bir kerede, değişen uç taramasında altı ayrı yapıyı durdurarak elenir.
Sonraki Adım
İki denetim de aynı varsayımla çalıştı: taranan şey ekibin yazdığı koddur. Oysa çalışan sürecin büyük bölümü başka yerde yazılmış bileşenlerden gelir ve bu bileşenlerin açıkları zaten bilinir — biri onları bulmuş, kaydetmiş ve yayımlamıştır. O halde iş bulmak değil eşleştirmektir: elde bir bağımlılık çizgesi ve bir kayıt listesi vardır, sorulan soru hangi kaydın hangi bileşeni gerçekten etkilediğidir. Bu eşleştirmenin kendi yanlış pozitifi ve kendi yanlış negatifi vardır. Sonraki ders bağımlılık çizgesini bilinen açık kataloğuyla eşleştirir, doğrudan ve geçişli etkilenen bileşeni ayırır ve çağrılmayan bileşendeki açığın neden başka bir sıraya girdiğini sayar.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.