Ders 03 / 15
Kimlik Bilgisi Doldurma Savunması
Bir saatlik oturum açma denemesi kaydında hesap ve adres eşiklerinin ayrı ayrı koşturulması ve hangi eşiğin hangi saldırı biçimini durdurduğunun ayrılması: kaba kuvveti hesap eşiği, yoğun kaynaklı püskürtmeyi adres eşiği durdururken hesap başına tek deneme yapan doldurmanın ikisinden de geçmesi, engellenen deneme sayısıyla önlenen hesap ele geçirme sayısı arasındaki fark, paylaşılan çıkış adresinin ve hedef alınan meşru hesabın kilitlenmesi.
İçindekiler
Önceki ders hız sayacını tek bir anahtara bağladı ve o anahtarın ne olduğunu hiç sormadı. Oturum açma denemesi dizisinin durumu da bu yüzden belirsiz kaldı: saniyedeki hız biliniyordu, ama denemelerin kaç hesaba ve kaç adrese dağıldığı bilinmiyordu. Aynı toplam hız tek hesaba yüklendiğinde başka, dört bin hesaba birer deneme olarak dağıtıldığında bambaşka bir saldırı biçimidir. Bu ders eşiği hız ekseninden çıkarıp hesap ve adres eksenine taşıyor.
İki biçimin adı ayrı tutulur. Kimlik bilgisi doldurma, başka bir yerden sızmış hesap–parola çiftlerinin bu sistemde denenmesidir; hesap başına genellikle tek deneme yapılır ve umut, aynı parolanın burada da kullanılmış olmasıdır. Püskürtme ise az sayıda yaygın parolanın çok hesapta denenmesidir; hesap başına birkaç deneme düşer. İkisi de hesap ekseninde seyrektir, ama adres ekseninde farklı yoğunluk gösterirler ve savunma tam olarak bu farkı ölçebilir.
Üç Saldırı Biçimi, İki Eksen
Aşağıdaki her şey node ile kurulan bir modeldir. Hiçbir deneme gerçekten yapılmıyor, hiçbir
kimlik bilgisi listesi ya da deneme aracı yazılmıyor; saldırı yalnız bir kayıt satırı biçiminde,
hedef hesap ve kaynak adres alanlarıyla temsil ediliyor.
- US11. Bin dokuz yüz gerçek hesap, saldırı biçimleri ve sayıları kurgusal ölçüm ağına aittir. Kayıt görünür tohumla üretiliyor; üreteç kendi yazıldığı için koşum yinelenebilir.
- US12. Bir denemenin başarılı olması modelde hesabın özelliğinden türer: doldurma yalnız parolası sızmış çifte ait hesapta, püskürtme yalnız parolası yaygın olan hesapta başarılıdır.
- US13. Eşik, pencere içinde biriken başarısız deneme sayısına bakar; başarılı deneme sayacı artırmaz. Üretimdeki yaygın davranış budur ve bu dersin ana bulgusunun kaynağıdır.
- US14. Meşru başarısızlık oranı yüzde on ikidir ve saha ekibinin tamamı tek bir paylaşılan çıkış adresinden geliyor. Otuz abone de parolasını unutup art arda deniyor.
- US15. Eşik aşıldığında sonraki denemeler engelleniyor; hesap kilidi ile adres kısması aynı sonucu veriyor. Gerçekte ikisinin çözülme süresi farklıdır, sayıların yönünü değiştirmez.
// giris.mjs — bir saatlik oturum acma denemesi kaydi; model, gercek deneme uretilmiyor export const TOHUM = 20250804; let s = TOHUM; const rnd = () => (s = (s * 48271) % 2147483647) / 2147483647; const tam = (n) => Math.floor(rnd() * n); // gercek hesaplar: ikinci etken kayitli mi, parolasi sizmis cifte mi ait, parolasi yaygin mi export const HESAP = []; for (let i = 0; i < 1900; i++) HESAP.push({ ad: `hesap-${i}`, etken: rnd() < 0.86, tekrar: rnd() < 0.038, yaygin: rnd() < 0.009 }); export const HARITA = new Map(HESAP.map((h) => [h.ad, h])); // [dakika, hesap, adres, sinif, basarili] export function kayitUret() { const k = []; for (let i = 0; i < 500; i++) // saha ekibi: tek paylasilan cikis adresi k.push([tam(60), HESAP[tam(380)].ad, 'paylasilan-cikis', 'mesru', rnd() > 0.12]); for (let i = 0; i < 1900; i++) // portal aboneleri: dagitik adres k.push([tam(60), HESAP[380 + tam(1520)].ad, `abone-adres-${tam(900)}`, 'mesru', rnd() > 0.12]); for (let i = 0; i < 30; i++) { // parolasini unutan abone: ardisik deneme const n = 6 + tam(4), t0 = tam(50), ad = `abone-adres-${tam(900)}`, h = HESAP[380 + tam(1520)].ad; for (let j = 0; j < n; j++) k.push([t0 + j, h, ad, 'mesru', j === n - 1]); } for (let i = 0; i < 4000; i++) { // doldurma: hesap basina tek deneme, 400 adres const h = i < 1900 ? HESAP[i] : null; k.push([tam(60), h ? h.ad : `yok-${i}`, `doldurma-adres-${i % 400}`, 'doldurma', Boolean(h && h.tekrar)]); } for (let r = 0; r < 3; r++) // puskurtme: uc tur, hesap basina birer deneme for (let i = 0; i < 1900; i++) k.push([5 + r * 20 + tam(5), HESAP[i].ad, `puskurtme-adres-${i % 40}`, 'puskurtme', HESAP[i].yaygin && r === i % 3]); for (let i = 0; i < 600; i++) // kaba kuvvet: tek hesap, tek adres k.push([10 + Math.floor(i / 120), HESAP[7].ad, 'kaba-adres', 'kabakuvvet', false]); return k.sort((a, b) => a[0] - b[0]); } // esik: pencere icinde biriken basarisiz deneme sayisi; asilinca sonraki denemeler engellenir export function olc(kayit, hesapEsigi, adresEsigi) { const hf = new Map(), af = new Map(), kilit = new Set(); const tur = {}, gec = []; let engelMesru = 0, mesruHesapla = 0, mesruAdresle = 0, engelKotu = 0, gecenKotu = 0; for (const [, hesap, adres, sinif, basarili] of kayit) { const h = hf.get(hesap) || 0, a = af.get(adres) || 0; const engel = h >= hesapEsigi || a >= adresEsigi; if (sinif === 'mesru') { if (engel) { engelMesru++; kilit.add(hesap); h >= hesapEsigi ? mesruHesapla++ : mesruAdresle++; } else if (!basarili) { hf.set(hesap, h + 1); af.set(adres, a + 1); } continue; } tur[sinif] = tur[sinif] || { engellenen: 0, gecen: 0, eleGecen: 0 }; if (engel) { engelKotu++; tur[sinif].engellenen++; continue; } gecenKotu++; tur[sinif].gecen++; if (basarili) { tur[sinif].eleGecen++; gec.push([hesap, sinif]); } else { hf.set(hesap, h + 1); af.set(adres, a + 1); } } return { engelMesru, mesruHesapla, mesruAdresle, kilitliHesap: kilit.size, engelKotu, gecenKotu, eleGecen: gec, tur }; }
Hangi Eşik Neyi Görüyor
// kosum.mjs — ayni kayit, degisen tek sey hesap ve adres esikleri import { TOHUM, kayitUret, olc } from './giris.mjs'; const s = (x, n) => String(x).padStart(n); const K = kayitUret(); const say = (f) => K.filter(f).length; const I = Infinity; console.log(`tohum ${TOHUM}; 60 dakika, ${K.length} deneme` + ` (${say((r) => r[3] === 'mesru')} mesru / ${say((r) => r[3] !== 'mesru')} kotu)\n`); console.log(`${'saldiri bicimi'.padEnd(14)}${s('deneme', 8)}${s('hesap', 7)}${s('adres', 7)}` + `${s('deneme/hesap', 14)}${s('deneme/adres', 14)}${s('ele gecen hesap', 17)}`); for (const t of ['doldurma', 'puskurtme', 'kabakuvvet']) { const r = K.filter((x) => x[3] === t); const h = new Set(r.map((x) => x[1])).size, a = new Set(r.map((x) => x[2])).size; console.log(`${t.padEnd(14)}${s(r.length, 8)}${s(h, 7)}${s(a, 7)}` + `${s((r.length / h).toFixed(1), 14)}${s((r.length / a).toFixed(1), 14)}` + `${s(r.filter((x) => x[4]).length, 17)}`); } const AYAR = [['esik yok', I, I], ['hesap 5', 5, I], ['adres 50', I, 50], ['hesap 5 + adres 50', 5, 50], ['hesap 3 + adres 20', 3, 20]]; console.log(`\n${'ayar'.padEnd(20)}${s('engellenen kotu', 17)}${s('gecen kotu', 12)}` + `${s('ele gecen', 11)}${s('engellenen mesru', 18)}${s('kilitli hesap', 15)}`); for (const [ad, he, ae] of AYAR) { const r = olc(K, he, ae); console.log(`${ad.padEnd(20)}${s(r.engelKotu, 17)}${s(r.gecenKotu, 12)}${s(r.eleGecen.length, 11)}` + `${s(r.engelMesru, 18)}${s(r.kilitliHesap, 15)}`); } console.log(`\nsaldiri bicimine gore engellenen deneme:`); console.log(`${'ayar'.padEnd(20)}${['doldurma', 'puskurtme', 'kabakuvvet'].map((t) => s(t, 12)).join('')}`); for (const [ad, he, ae] of AYAR.slice(1)) { const r = olc(K, he, ae); console.log(`${ad.padEnd(20)}` + ['doldurma', 'puskurtme', 'kabakuvvet'].map((t) => s(r.tur[t].engellenen, 12)).join('')); }
tohum 20250804; 60 dakika, 12923 deneme (2623 mesru / 10300 kotu) saldiri bicimi deneme hesap adres deneme/hesap deneme/adres ele gecen hesap doldurma 4000 4000 400 1.0 10.0 57 puskurtme 5700 1900 40 3.0 142.5 13 kabakuvvet 600 1 1 600.0 600.0 0 ayar engellenen kotu gecen kotu ele gecen engellenen mesru kilitli hesap esik yok 0 10300 70 0 0 hesap 5 669 9631 69 220 91 adres 50 4244 6056 63 227 164 hesap 5 + adres 50 4301 5999 62 364 194 hesap 3 + adres 20 5512 4788 57 613 315 saldiri bicimine gore engellenen deneme: ayar doldurma puskurtme kabakuvvet hesap 5 18 55 596 adres 50 0 3694 550 hesap 5 + adres 50 11 3694 596 hesap 3 + adres 20 15 4899 598
İlk tablo iki ekseni yan yana koyuyor ve ayrım orada okunuyor. Kaba kuvvet hesap ekseninde altı yüz denemeyle uçtadır; hesap eşiği beşe kurulduğunda beş yüz doksan altı denemesi engellenir, yani bu biçim tek bir eşikle bitirilmiş sayılır. Püskürtme hesap ekseninde seyrektir — hesap başına üç deneme, eşiğin altında — ama kırk adrese sıkıştığı için adres başına yüz kırk iki denemeye çıkar; adres eşiği elliye kurulduğunda üç bin altı yüz doksan dört denemesi engellenir. Doldurma her iki eksende de eşiğin altındadır: hesap başına bir deneme, adres başına on. Adres eşiği bu biçimden sıfır deneme engelliyor.
Alt tablodaki doldurma sütununda görünen on beş ile on sekiz, doldurmanın kendi yoğunluğundan değil, aynı hesapların püskürtmeye de hedef olmasından geliyor: iki biçimin başarısızlıkları aynı hesap sayacında toplanınca birkaç hesap eşiği aşıyor. Yani doldurma ancak yanında başka bir biçim varsa dolaylı olarak görülüyor.
Orta tablonun son iki sütunu bedeli yazıyor. Hesap eşiği beşte doksan bir meşru hesap kilitleniyor ve bunların yalnız otuzu parolasını unutan abonedir; kalanı saldırının hedefi olduğu için kilitlendi. Hesabına yönelik başarısız denemeler sayacı doldurdu ve hesabın gerçek sahibi giremez oldu. Adres eşiği ellide ise iki yüz yirmi yedi meşru deneme kesiliyor; hepsi paylaşılan çıkış adresinden gelen saha ekibine ait, çünkü o adresten geçen beş yüz denemenin yüzde on ikilik başarısız payı eşiği tek başına aşıyor. Adres eşiği bir hesabı değil, bir ekibi kesiyor.
Eşiğin Önlemediği Ele Geçirme
Yukarıdaki tabloda gözden kaçması en muhtemel sütun “ele geçen” sütunudur. Gösterge tablosunda öne çıkan sayı engellenen denemedir ve o sayı ayar sıkılaştıkça yüzlerden binlere tırmanır. Ele geçen sütunu ise neredeyse yerinde duruyor: eşiksiz koşumda yetmiş hesap ele geçiyor, en sıkı ayarda elli yedi. Beş bin beş yüz on iki deneme engellendi ve ele geçirme yalnız on üç azaldı. Bu iki sayının aynı tabloda bulunması gerekir, çünkü savunmanın amacı denemeyi azaltmak değil, hesabın ele geçmesini önlemektir.
// asama.mjs — ayni hesap ele gecirmeyi daha erken hangi asama kapatir import { HESAP, HARITA, kayitUret, olc } from './giris.mjs'; const s = (x, n) => String(x).padStart(n); const K = kayitUret(), I = Infinity; const mesru = K.filter((r) => r[3] === 'mesru').length; const taban = olc(K, I, I), siki = olc(K, 3, 20); const etkensiz = (r) => r.eleGecen.filter(([ad]) => !HARITA.get(ad).etken).length; const belirleme = HESAP.filter((h) => h.tekrar || h.yaygin).length; console.log(`${'onlem'.padEnd(34)}${s('asama', 16)}${s('ele gecen', 11)}${s('mesru bedel', 28)}`); for (const [a, b, c, d] of [ ['onlem yok', '-', taban.eleGecen.length, '0'], ['hesap 3 + adres 20 esigi', 'calisma zamani', siki.eleGecen.length, `${siki.engelMesru} deneme, ${siki.kilitliHesap} hesap`], ['ikinci etken, kapsam %86', 'kod', etkensiz(taban), `${mesru} oturumda ek adim`], ['esik ve ikinci etken', 'ikisi', etkensiz(siki), `${siki.engelMesru} deneme, ek adim`], ['parola belirlemede cift denetimi', 'tasarim', 0, `${belirleme} hesapta parola degisir`], ]) console.log(`${a.padEnd(34)}${s(b, 16)}${s(c, 11)}${s(d, 28)}`); console.log(`\nikinci etken kapsami ${HESAP.filter((h) => h.etken).length}/${HESAP.length};` + ` kapsam disindaki ${HESAP.filter((h) => !h.etken).length} hesap her ayarda savunmasiz kaliyor\n`); console.log(`${'ayar'.padEnd(20)}${s('hesap esigi kesti', 19)}${s('adres esigi kesti', 19)}` + `${s('kilitli mesru hesap', 21)}`); for (const [ad, he, ae] of [['hesap 5', 5, I], ['adres 50', I, 50], ['hesap 5 + adres 50', 5, 50], ['hesap 3 + adres 20', 3, 20]]) { const r = olc(K, he, ae); console.log(`${ad.padEnd(20)}${s(r.mesruHesapla, 19)}${s(r.mesruAdresle, 19)}${s(r.kilitliHesap, 21)}`); } console.log(`\npaylasilan cikis adresinden gelen mesru deneme: ` + `${K.filter((r) => r[3] === 'mesru' && r[2] === 'paylasilan-cikis').length}`);
onlem asama ele gecen mesru bedel onlem yok - 70 0 hesap 3 + adres 20 esigi calisma zamani 57 613 deneme, 315 hesap ikinci etken, kapsam %86 kod 5 2623 oturumda ek adim esik ve ikinci etken ikisi 4 613 deneme, ek adim parola belirlemede cift denetimi tasarim 0 70 hesapta parola degisir ikinci etken kapsami 1639/1900; kapsam disindaki 261 hesap her ayarda savunmasiz kaliyor ayar hesap esigi kesti adres esigi kesti kilitli mesru hesap hesap 5 220 0 91 adres 50 0 227 164 hesap 5 + adres 50 138 226 194 hesap 3 + adres 20 220 393 315 paylasilan cikis adresinden gelen mesru deneme: 500
Üst tablo kursun ana sorusunu bu ders için yanıtlıyor. Çalışma zamanı eşiği kötü denemenin yüzde elli üçünü engelledi ve ele geçirmeyi yetmişten elli yediye indirdi: önlenen ele geçirme oranı yüzde on dokuz. Bunun nedeni eşiğin ayarında değil, tanımındadır. Eşik başarısızlıkları sayar; doldurmada başarılı deneme hesabın gördüğü ilk denemedir ve o an sayaç boştur. Sayaç ancak başarısızlıklar biriktikten sonra dolar, yani eşik geç kalmış olmayı yapısal olarak taşır.
Aynı ele geçirme kod aşamasında ikinci etkenle yetmişten beşe iniyor. Beşin sıfır olmamasının nedeni de tabloda yazılı: ikinci etken kapsamı bin altı yüz otuz dokuz hesaptır ve kapsam dışında kalan iki yüz altmış bir hesap hiçbir eşik ayarında korunmuyor. Kapsam, bu önlemin tek gerçek ayarıdır. Bedeli ise iki bin altı yüz yirmi üç meşru oturumda ek bir adımdır; kesilen oturum yoktur, geciken oturum vardır.
Tasarım aşamasındaki önlem daha da ucuza kapatıyor. Parola belirlenirken sızmış çift ve yaygın parola denetimi yapıldığında doldurmanın da püskürtmenin de başarı koşulu ortadan kalkar: ele geçen hesap sıfırdır. Bedeli yetmiş hesabın parola değiştirmesidir ve bu bir defalıktır. Çalışma zamanı eşiği aynı kalem için her gün altı yüz on üç meşru denemeyi kesiyor ve üç yüz on beş hesabı kilitliyor; tasarımdaki denetim kalemi bir kez kapatıyor.
Alt tablo yanlış kesmenin nereye düştüğünü ayırıyor. Hesap eşiği kesmeyi hesap sahibine, adres eşiği ise bir ağ konumunun arkasındaki herkese yükler. Paylaşılan çıkış adresi arkasında beş yüz meşru deneme duruyor ve adres eşiği yirmiye indirildiğinde bu adresten kesilen deneme üç yüz doksan üçe çıkıyor. Adres eşiği, hedeflenen saldırgandan çok o adresin arkasındaki ekibin büyüklüğüne göre ceza kesiyor.
Özet
- Üç saldırı biçimi iki eksende ayrışıyor: kaba kuvvet hesap başına 600 denemeyle hesap eşiğinde (596 engellendi), püskürtme adres başına 142,5 denemeyle adres eşiğinde (3.694 engellendi) durur; doldurma hesap başına 1,0 ve adres başına 10,0 denemeyle ikisinin de altındadır.
- Adres eşiği doldurmadan sıfır deneme engelledi. Alt tabloda görünen 15–18 deneme, aynı hesapların püskürtmeye de hedef olmasından gelen dolaylı bir yakalamadır.
- Eşiksiz koşumda 70 hesap ele geçti; en sıkı ayarda 57. Engellenen deneme 5.512’ye çıkarken önlenen ele geçirme yalnız 13’tür (yüzde 19), çünkü eşik başarısızlıkları sayar ve doldurmada başarılı deneme hesabın ilk denemesidir.
- Yanlış kesme iki ayrı yere düşüyor: hesap eşiği 5’te kilitlenen 91 hesabın yalnız 30’u parolasını unutan abonedir, kalanı saldırının hedefi olduğu için kilitlendi. Adres eşiği 50’de kesilen 227 denemenin tamamı paylaşılan çıkış adresindeki saha ekibine aittir.
- Aynı ele geçirme kod aşamasında ikinci etkenle 70’ten 5’e (kalan 5, kapsam dışındaki 261 hesaptan), tasarımda parola belirleme denetimiyle 0’a iniyor. Tasarımdaki bedel 70 hesapta bir defalık parola değişimi; çalışma zamanı eşiğinin bedeli her gün 613 kesilen meşru denemedir.
Sonraki Adım
Bu dersin eşikleri de önceki iki dersin kuralları ve sayaçları da tek tek isteklere ya da denemelere bakıyordu. Üçünün de ortak varsayımı, savunmanın istekle birlikte gelen bilgiye bakmasıydı. Oysa çalışma zamanında sunucunun verdiği ve isteğin içeriğiyle hiç ilgisi olmayan bir karar daha vardır: yanıtla birlikte gönderilen güvenlik başlıkları. Bunlar bir isteği durdurmaz; tarayıcının ne yapmasına izin verildiğini belirler ve tek bir yerden yönetildiklerinde arkalarındaki bütün uygulamaları aynı anda kapsarlar. Sonraki ders bu katmanı yakaladığı istekle değil, kapsadığı uygulama sayısıyla ölçüyor ve asıl soruyu kapsamın dışında kalan uca soruyor.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.