Ders 01 / 12
Statik Uygulama Güvenlik Testi
720 kod noktasında statik çözümlemenin üç sayısı: eşik 0,20'den 0,80'e çıkarken bulgu 418'den 56'ya, yanlış pozitif 317'den 6'ya iner ama gerçek bulgu da 101'den 50'ye düşer; 62 açık nokta eşikten bağımsız olarak aracın görüş alanı dışındadır. Aynı kuyruk geliştiriciye verildiğinde 0,20 eşiği 101 gerçek bulgunun 80'ini hiç açılmadan kapattırır, 0,65 eşiği 72 gerçek bulgunun 65'ini inceletir; gerçek bulgu başına inceleme süresi 26,9 dakikadan 10,2 dakikaya iner.
İçindekiler
Güvenli Kodlama kursu on yedi ölçüm boyunca aynı soruyu sordu: bir savunma kalıbı kaç noktayı kendiliğinden kapatıyor, kaç nokta dışarıda kalıyor. Bütün o envanterler elle kurulmuştu ve kod değiştikçe eskiyen tek bir fotoğraftı. Bu kurs aynı soruyu her değişiklikte soran düzeneği ölçer: hatta koşan otomatik denetimler.
Bir denetimin değeri “kaç bulgu çıkardığı” değildir. Üç sayı birlikte okunur: kaç kalem tarandı ve kaç bulgu çıktı, bulguların kaçı gerçek ve kaçı yanlış pozitif, gerçekte var olup hiç görülmeyen kaç kalem kaldı. Üçüncüsü yazılmadıkça ilk ikisi bir güvence değil bir yanılsamadır. Dördüncü bir sütun da vardır ve o hattadır: denetim kaç dakika ekliyor, kaç yapıyı durduruyor, kaçını yanlış yere durduruyor. Bu ders kursun ölçü birimini bu dört sütunla kurar ve statik çözümlemeyi örnek alır.
- OD1. Kod tabanı üç depodur: abone portalı, sayaç alım servisi ve ikisinin kullandığı ortak kitaplık. Depolar da kod tabanı da kurgudur; aşağıdaki sayılar bir modelin çıktısıdır.
- OD2. Bir nokta, bir aracın kural yazabildiği kod konumudur. Her noktanın gerçekte açık olup olmadığı modelin doğrusudur; araç bunu bilmez.
- OD3. Statik uygulama güvenlik testi (static application security testing), kaynak kodu çalıştırmadan çözümleyip açık adayı üreten denetimdir. Araç her gördüğü noktaya bir güven puanı verir; eşik, bulgu olarak raporlanacak en küçük puandır.
- OD4. Her nokta sınıfının bir kapsam değeri vardır: aracın o sınıfta hiç aday üretebilme olasılığı. Kapsamı sıfır olan sınıf eşikten bağımsız olarak görülmez; bu, aracın görüş alanı dışıdır.
- OD5. Üç sayı ayrı yazılır: gerçek bulgu, yanlış pozitif (bulgu olup açık olmayan) ve görülmeyen kalem (açık olup bulgu olmayan).
- OD6. Hat bedeli model dakikasıyla verilir: yapı başına 4 dakika tarama, yanlış yere bloke edilen yapı başına 25 dakika insan zamanı, bulgu başına 6 dakika inceleme. Süreler veridir, bu sayfada yapılmış bir ölçüm değildir.
Eşik Üç Sayıyı Birlikte Oynatır
// tarama.mjs — statik cozumleme aracinin bulgu kumesi; esik degistikce gercek, // yanlis pozitif ve gorulmeyen kalem birlikte olculur. MODELDIR, kod tabani kurgudur. export const TOHUM = 20260311, NOKTA = 720, YAPI = 140, TARAMA_DK = 4, YANLIS_DK = 25; export const INCELEME_DK = 6; const uretec = (t) => { let s = t >>> 0; return () => (s = (s * 1664525 + 1013904223) >>> 0) / 4294967296; }; const r = uretec(TOHUM); // kapsam = aracin o sinifta hic aday uretebilme olasiligi; 0 olan sinif esikten // bagimsiz gorulmez, bu aracin gorus alani disidir. ayirt = puanin ayirt etme gucu. export const SINIF = [ { ad: "dizgi birlestirme", pay: 0.26, acik: 0.22, kapsam: 0.95, ayirt: 0.62 }, { ad: "dis komut cagrisi", pay: 0.08, acik: 0.35, kapsam: 0.90, ayirt: 0.55 }, { ad: "cozumleme sinirli", pay: 0.14, acik: 0.20, kapsam: 0.40, ayirt: 0.30 }, { ad: "kriptografi kullanimi", pay: 0.12, acik: 0.28, kapsam: 0.85, ayirt: 0.70 }, { ad: "yapilandirma sabiti", pay: 0.22, acik: 0.10, kapsam: 0.80, ayirt: 0.25 }, { ad: "erisim denetimi", pay: 0.18, acik: 0.30, kapsam: 0.00, ayirt: 0.00 }, ]; const sinifSec = (u) => { let t = 0; for (const s of SINIF) { t += s.pay; if (u < t) return s; } return SINIF.at(-1); }; // Her nokta bir kod konumudur; acik alani modelin dogrusudur, arac onu bilmez. // Gorulen noktaya bir puan verilir: acik noktalar yukari, temiz noktalar asagi kayar. export const NOKTALAR = Array.from({ length: NOKTA }, (_, i) => { const s = sinifSec(r()); const acik = r() < s.acik, gordu = r() < s.kapsam, taban = (r() + r()) / 2; const puan = !gordu ? null : acik ? Math.min(0.99, taban + s.ayirt * 0.45) : Math.max(0.01, taban - s.ayirt * 0.20); return { i, sinif: s.ad, acik, puan, yapi: Math.floor(r() * YAPI) }; }); export function esikte(t) { const bulgu = NOKTALAR.filter((n) => n.puan !== null && n.puan >= t); const gercek = bulgu.filter((n) => n.acik), yanlis = bulgu.filter((n) => !n.acik); const kacan = NOKTALAR.filter((n) => n.acik && !bulgu.includes(n)); const korNokta = kacan.filter((n) => n.puan === null); const bulguluYapi = new Set(bulgu.map((n) => n.yapi)); const gercekliYapi = new Set(gercek.map((n) => n.yapi)); const yanlisBloke = [...bulguluYapi].filter((y) => !gercekliYapi.has(y)).length; return { esik: t, bulgu, gercek: gercek.length, yanlis: yanlis.length, kacan: kacan.length, korNokta: korNokta.length, bloke: bulguluYapi.size, yanlisBloke, dk: YAPI * TARAMA_DK + yanlisBloke * YANLIS_DK }; } export const ESIKLER = [0.20, 0.35, 0.50, 0.65, 0.80]; 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 ACIK = NOKTALAR.filter((n) => n.acik).length; const KOR = NOKTALAR.filter((n) => n.acik && n.puan === null).length; console.log(`model (tohum ${TOHUM}): ${NOKTA} nokta, ${ACIK} tanesi gercekte acik, ` + `${YAPI} yapi; ${KOR} acik nokta aracin gorus alani disinda`); const y1 = gen([7, 8, 9, 9, 12, 11, 8, 14, 9]); console.log("\n" + y1(["esik", "bulgu", "gercek", "yanlis+", "gorulmeyen", "gorus disi", "bloke", "yanlis bloke", "hat dk"])); for (const t of ESIKLER) { const o = esikte(t); console.log(y1([t.toFixed(2), o.bulgu.length, o.gercek, o.yanlis, o.kacan, o.korNokta, o.bloke, o.yanlisBloke, o.dk])); } const o = esikte(0.50); const y2 = gen([22, 8, 7, 8, 9, 9, 12]); console.log("\nesik 0.50'de sinif dagilimi"); console.log(y2(["nokta sinifi", "nokta", "acik", "bulgu", "gercek", "yanlis+", "gorulmeyen"])); for (const s of SINIF) { const l = NOKTALAR.filter((n) => n.sinif === s.ad); const b = o.bulgu.filter((n) => n.sinif === s.ad); console.log(y2([s.ad, l.length, l.filter((n) => n.acik).length, b.length, b.filter((n) => n.acik).length, b.filter((n) => !n.acik).length, l.filter((n) => n.acik).length - b.filter((n) => n.acik).length])); } }
model (tohum 20260311): 720 nokta, 163 tanesi gercekte acik, 140 yapi; 62 acik nokta aracin gorus alani disinda esik bulgu gercek yanlis+ gorulmeyen gorus disi bloke yanlis bloke hat dk 0.20 418 101 317 62 62 132 61 2085 0.35 332 99 233 64 62 130 59 2035 0.50 225 92 133 71 62 112 46 1710 0.65 120 72 48 91 62 80 21 1085 0.80 56 50 6 113 62 47 4 660 esik 0.50'de sinif dagilimi nokta sinifi nokta acik bulgu gercek yanlis+ gorulmeyen dizgi birlestirme 199 47 94 41 53 6 dis komut cagrisi 52 16 24 15 9 1 cozumleme sinirli 99 23 18 8 10 15 kriptografi kullanimi 91 22 39 19 20 3 yapilandirma sabiti 144 18 50 9 41 9 erisim denetimi 135 37 0 0 0 37
İlk tablo eşiğin tek bir düğme olmadığını gösteriyor. 0,20’de araç 418 bulgu çıkarıyor ve bunların 317’si yanlış pozitif — raporun yüzde 75,8’i. Eşik 0,80’e çekildiğinde yanlış pozitif 6’ya iniyor, ama aynı hareket gerçek bulguyu da 101’den 50’ye düşürüyor. İki sayı aynı yöne gider; biri iyileşirken diğeri bozulur. Eşiği “gürültüyü azaltmak için” yükseltmek, aynı işlemle gerçek bulguların yarısını rapordan silmektir.
Beşinci sütun eşiğin hiç dokunamadığı yeri yazıyor. 163 açık noktanın 62’si her eşikte görülmüyor; bu sayı 0,20’de de 0,80’de de aynıdır. Bunlar aracın yanlış negatifleridir ve kaynağı bir ayar hatası değil, çözümlemenin yapısıdır. Sınıf tablosu nedeni gösteriyor: erişim denetimi sınıfında 135 nokta ve 37 gerçek açık var, bulgu sayısı sıfır. Bir isteğin çağıranın yetkisine uygun olup olmadığı kaynak metninden okunamaz, çünkü kural kodda değil iş tanımındadır. Çözümlemesi sınırlı sınıf da benzerdir: 23 açığın 15’i görülmüyor, çünkü çağrı hedefi çalışma zamanında belirlenen kod aracın izleyemediği bir kenardır. Bir tarama raporunda “sıfır bulgu” yazan sınıf, ya temiz olan ya da hiç bakılmayan sınıftır; ikisini ayıran tek şey kapsam listesidir.
Son üç sütun hattı ölçüyor. 140 yapının 132’si 0,20 eşiğinde duruyor ve 61’i yalnız yanlış pozitif yüzünden duruyor: hattın neredeyse yarısı boşuna bloke. Bu 61 durdurma model hesabında 1 525 dakika insan zamanına karşılık geliyor ve toplam hat bedelini 2 085 dakikaya çıkarıyor. Eşik 0,65’e alındığında yanlış bloke 21’e, toplam 1 085 dakikaya iniyor. Aynı tabloda gerçek bulgunun 101’den 72’ye indiği yazılıdır — kazanç bedava değildir.
Sınıf tablosu bir ayrım daha yazıyor. Yapılandırma sabiti sınıfı 144 noktayla ikinci büyük kaynaktır ama yalnız 18’i gerçekten açıktır; 0,50 eşiğinde 50 bulgu üretir ve bunların 41’i yanlış pozitiftir. Kriptografi kullanımı sınıfı 91 noktayla daha küçüktür ve 39 bulgunun 19’u gerçektir. İki sınıfın eşiği ayrı ayarlanabilseydi rapor aynı gerçek bulguyu çok daha az gürültüyle verirdi; tek bir küresel eşik, en gürültülü sınıfın davranışını bütün rapora taşır.
Bu yüzden bir taramanın çıktısı yalnız bulgu listesi değildir. Yanında iki kayıt daha durmalıdır: hangi nokta sınıflarının tarandığı ve hangi eşikle raporlandığı. Birincisi olmadan sıfır bulgunun anlamı belirsizdir, ikincisi olmadan iki koşumun sayıları karşılaştırılamaz. Aynı kod tabanında eşiği 0,80’den 0,50’ye indiren bir koşum bulguyu 56’dan 225’e çıkarır; kodda hiçbir şey değişmemiştir.
Bulgu Yorgunluğu
Buraya kadar sayılan her şey aracın çıktısıdır. Bulgu bir işe yarayacaksa bir kişinin onu açması gerekir, ve o kişinin dikkati sınırsız değildir.
// yorgunluk.mjs — ayni bulgu kumesi gelistiriciye gidiyor: her yanlis pozitif // inceleme olasiligini dusurur, gercek bulgu geri yukseltir. MODELDIR. import { esikte, ESIKLER, INCELEME_DK, TOHUM } from "./tarama.mjs"; const uretec = (t) => { let s = t >>> 0; return () => (s = (s * 1664525 + 1013904223) >>> 0) / 4294967296; }; // Guven, gelistiricinin siradaki bulguya bakma olasiligidir. Bakilmayan bulgu // hicbir bilgi vermez, bu yuzden dusen guven kendini onarmaz. function kuyruk(t) { const r = uretec(TOHUM + 7); const liste = esikte(t).bulgu.slice().sort((a, b) => a.yapi - b.yapi || a.i - b.i); let guven = 1, incelenen = 0, incelenenGercek = 0, atlananGercek = 0; for (const n of liste) { if (r() >= guven) { if (n.acik) atlananGercek++; continue; } incelenen++; if (n.acik) { incelenenGercek++; guven = Math.min(1, guven + 0.10); } else { guven = Math.max(0.10, guven - 0.055); } } return { bulgu: liste.length, gercek: liste.filter((n) => n.acik).length, incelenen, incelenenGercek, atlananGercek, guven, dk: incelenen * INCELEME_DK }; } const gen = (B) => (h) => h.map((v, j) => (j ? String(v).padStart(B[j]) : String(v).padEnd(B[0]))).join(""); const y = gen([7, 8, 9, 11, 12, 12, 11, 9, 11]); console.log(`bulgu basina inceleme ${INCELEME_DK} model dakikasi; guven 1.00'den baslar`); console.log(y(["esik", "bulgu", "gercek", "incelenen", "inc. gercek", "atlanan g.", "son guven", "inc. dk", "dk/gercek"])); for (const t of ESIKLER) { const o = kuyruk(t); console.log(y([t.toFixed(2), o.bulgu, o.gercek, o.incelenen, o.incelenenGercek, o.atlananGercek, o.guven.toFixed(2), o.dk, o.incelenenGercek ? (o.dk / o.incelenenGercek).toFixed(1) : "-"])); } const dusuk = kuyruk(0.20), orta = kuyruk(0.65); console.log(`\nesik 0.20 esik 0.65'ten ${dusuk.gercek - orta.gercek} fazla gercek bulgu ` + `cikariyor ama ${orta.incelenenGercek - dusuk.incelenenGercek} tanesi daha az inceleniyor`); console.log(`0.20'de ${dusuk.atlananGercek} gercek bulgu hic acilmadan kapaniyor, ` + `0.65'te ${orta.atlananGercek}; gercek bulgu basina inceleme suresi ` + `${(dusuk.dk / dusuk.incelenenGercek).toFixed(1)} dakikadan ` + `${(orta.dk / orta.incelenenGercek).toFixed(1)} dakikaya iniyor`);
bulgu basina inceleme 6 model dakikasi; guven 1.00'den baslar esik bulgu gercek incelenen inc. gercek atlanan g. son guven inc. dk dk/gercek 0.20 418 101 94 21 80 0.10 564 26.9 0.35 332 99 119 36 63 0.18 714 19.8 0.50 225 92 198 82 10 0.93 1188 14.5 0.65 120 72 110 65 7 0.94 660 10.2 0.80 56 50 55 49 1 1.00 330 6.7 esik 0.20 esik 0.65'ten 29 fazla gercek bulgu cikariyor ama 44 tanesi daha az inceleniyor 0.20'de 80 gercek bulgu hic acilmadan kapaniyor, 0.65'te 7; gercek bulgu basina inceleme suresi 26.9 dakikadan 10.2 dakikaya iniyor
Tablo, ilk tablodaki sıralamayı tersine çeviriyor. Araç ölçüsüyle 0,20 en iyi eşiktir: 101 gerçek bulgu, hepsinden fazlası. Kuyruk ölçüsüyle en kötü eşiktir: o 101 bulgunun yalnız 21’i inceleniyor, 80’i hiç açılmadan kapanıyor. 0,65 eşiği 29 gerçek bulgu daha az çıkarıyor ama 44 gerçek bulgu daha fazla inceletiyor. Bulgu sayısı ile kapatılan açık sayısı aynı yönde gitmiyor.
Mekanizma güven sütununda yazılı. Güven, bir sonraki bulguya bakma olasılığıdır; her yanlış pozitif onu 0,055 düşürür, her gerçek bulgu 0,10 yükseltir. 0,20 eşiğinde kuyruğun başındaki yoğun yanlış pozitif dizisi güveni ilk yüz bulguda tabana indiriyor ve orada bırakıyor: son güven 0,10. Bir kez oraya inildiğinde onarım da durur, çünkü bakılmayan bulgu hiçbir bilgi vermez — gerçek bulgu güveni ancak açıldığında yükseltebilir. Bulgu yorgunluğu (alert fatigue) bu kilitlenmenin adıdır ve tek bir bulgu üzerinde görülmez; kuyruğun tamamı üzerinde ortaya çıkar.
Son sütun aynı olguyu maliyet tarafından yazıyor. 0,20’de gerçek bulgu başına 26,9 dakika inceleniyor, 0,80’de 6,7 dakika. Aradaki fark yanlış pozitiflerin açılıp kapatılmasına giden zamandır. Bu ders eşiğin doğru değerini vermez, çünkü doğru değer kod tabanına ve ekibin kapasitesine bağlıdır. Verdiği şey karar kuralıdır: bir eşik üç sayıyla birlikte raporlanır ve dördüncü olarak kuyruğun ne kadarının gerçekten açıldığı ölçülür. Bu dört sayı olmadan eşik değiştirmek, hangi sütunun ödediği bilinmeden yapılan bir takastır.
Özet
- Bir denetim üç sayıyla ölçülür: kaç bulgu çıktı, kaçı gerçek ve kaçı yanlış pozitif, gerçekte var olup hiç görülmeyen kaç kalem kaldı. Dördüncü sütun hattadır: eklenen dakika ve bloke yapı.
- Eşik iki sayıyı aynı yöne oynatır: 0,20’den 0,80’e çıkarken yanlış pozitif 317’den 6’ya inerken gerçek bulgu da 101’den 50’ye düşer.
- 163 açık noktanın 62’si her eşikte görülmez; erişim denetimi sınıfının 37 açığının hiçbiri raporda yoktur, çünkü kural kaynak metninde değil iş tanımındadır.
- Hat bedeli eşiğe bağlıdır: 0,20’de 140 yapının 61’i yalnız yanlış pozitif yüzünden bloke olur ve toplam 2 085 dakika, 0,65’te 21 yapı ve 1 085 dakika.
- Bulgu sayısı ile kapatılan açık sayısı aynı yönde gitmez: 0,20 eşiği 101 gerçek bulgunun 21’ini inceletirken 0,65 eşiği 72’nin 65’ini inceletir, çünkü yoğun yanlış pozitif güveni tabana indirir ve bakılmayan bulgu onu geri yükseltemez.
Sonraki Adım
Bu dersin bütün ölçümü kaynak metni üzerindeydi: aracın gördüğü şey dosyalardaki kod konumlarıydı ve görüş alanı dışında kalan 62 açık noktanın büyük bölümü, kararı çalışma zamanında verilen noktalardı. O noktalara ulaşmanın bir yolu var: uygulamayı ayağa kaldırıp dışarıdan istemek. Ama bu kez tarayıcının önünde başka bir sınır durur — çalışan bir uygulamanın her ucu istekle erişilebilir değildir. Bir bölümü oturum açmadan yanıt vermez, bir bölümü yalnız belirli bir sıra izlendiğinde ortaya çıkar. Sonraki ders çalışan uygulamayı tarayan aracın erişebildiği ve erişemediği yüzeyi ayırır ve aynı üç sayıyı bu kez uç kümesi üzerinde verir.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.