Ders 13 / 17
Kriptografi Kötüye Kullanımı
Kriptografi çağrı noktalarının kapsaması ve yanlış kullanımın somut sonucu: on dört çağrı noktasının yedisinin merkezî katmanın dışında kalması, aynı anahtarla aynı başlangıç değerini yeniden kullanmanın kırk konumu okunur hâle getirmesi, bütünlük etiketi olmayan şifrelemede biçim denetiminin yirmi dört değişiklikten dördünü kaçırması ve ilkel çağrı tek dosyaya kapatıldığında yeni noktanın kendiliğinden kapsanması.
İçindekiler
Önceki ders kimin ne yapabildiğini kodda denetledi: yetki matrisindeki uçlar sayıldı, eşzamanlı isteğin aynı kontrolden iki kez geçtiği durum ölçüldü. Bütün o denetimler bir şeye dayanıyordu. Oturumu bir imza taşıyordu, saha uygulamasının yükleme belirtecini bir anahtar üretmişti, yerel depo bir şifreleme çağrısıyla kilitlenmişti. Bu konu o kalemleri ele alır ve ilk soru onların nerede saklandığı değil, nasıl çağrıldığıdır.
Kriptografi Temelleri kursu algoritmaların özelliklerini ölçmüştü. Burada o özellikler girdidir; ölçülen şey çağrı noktasıdır. Bir kod tabanında kaç nokta kriptografi çağırıyor, kaçı merkezî bir katmandan geçiyor, dışarıda kalan noktaların yanlış ayarı somut olarak neye mal oluyor.
Kriptografi Çağrı Noktaları
KI1 — kurgu bölgesel ölçüm ağının kod tabanında on dört kriptografi çağrı noktası vardır. Bir bölümü merkezî bir kripto katmanının dört işlevini çağırır; kalanı ilkel çağrıyı doğrudan yapar ve kip, başlangıç değeri, tuz gibi ayarları kendi seçer. Aşağıdaki envanter bir modeldir; kaynak adları kurgudur.
// kripto/kapsama.mjs — kurgu olcum agi kod tabanindaki kriptografi cagri noktalari // envanteri (model). kaynak: cagriyi yapan bilesen; katman: merkezi kripto katmanindan // gecip gecmedigi; kusur: dogrudan cagrida secilen yanlis ayar. const noktalar = [ ['portal/oturum', 'sifreleme', false, 'etiketsiz kip'], ['portal/kayit', 'parola karmasi', true, null], ['portal/fatura-baglantisi', 'imza', true, null], ['alim/paket-acma', 'sifreleme', true, null], ['alim/yedek-yol', 'sifreleme', false, 'sabit baslangic degeri'], ['alim/es-zamanlama', 'karma', false, 'kendi karma zinciri'], ['saha/yukleme-belirteci', 'imza', false, null], ['saha/yerel-onbellek', 'sifreleme', false, null], ['rapor/aktarim', 'sifreleme', true, null], ['rapor/dosya-adi', 'karma', true, null], ['rapor/arsiv', 'sifreleme', false, 'etiketsiz kip'], ['is-emri/imza', 'imza', true, null], ['is-emri/eski-uc', 'parola karmasi', false, 'tuzsuz karma'], ['bildirim/geri-cagirma', 'imza', true, null], ]; const say = (kume, kural) => kume.filter(kural).length; const kusurlar = (kume) => { const g = {}; for (const [, , , k] of kume.filter(([, , katman]) => !katman)) if (k) g[k] = (g[k] ?? 0) + 1; return Object.entries(g).sort((a, b) => b[1] - a[1]); }; const yaz = (g, ...s) => console.log(s.map((v, i) => (g[i] < 0 ? String(v).padEnd(-g[i]) : String(v).padStart(g[i]))).join('')); const D = [-26, 10, 10, 12, 12]; yaz(D, 'islem turu', 'nokta', 'katmanda', 'disarida', 'kusurlu'); for (const tur of ['sifreleme', 'imza', 'karma', 'parola karmasi']) { const t = noktalar.filter(([, i]) => i === tur); yaz(D, tur, t.length, say(t, ([, , k]) => k), say(t, ([, , k]) => !k), say(t, ([, , , k]) => k)); } yaz(D, 'toplam', noktalar.length, say(noktalar, ([, , k]) => k), say(noktalar, ([, , k]) => !k), say(noktalar, ([, , , k]) => k)); console.log('\ndisarida kalan noktalarin kusur dagilimi:'); for (const [ad, n] of kusurlar(noktalar)) console.log(` ${ad.padEnd(24)}${n}`); // Yeni nokta eklendiginde ne oluyor: uc yeni cagri noktasi, iki ayri duzen. const yeni = [ ['saha/cevrimdisi-kuyruk', 'sifreleme'], ['portal/disa-aktarma', 'imza'], ['alim/olcum-arsivi', 'sifreleme'], ]; console.log('\nuc yeni cagri noktasi ekleniyor:'); const E = [-40, 8, 12, 18]; yaz(E, 'duzen', 'nokta', 'katmanda', 'yeni acik adayi'); // 1) Ilkel cagri serbest: yeni nokta katmani kullanmak zorunda degil. yaz(E, 'katman var, dogrudan cagri serbest', noktalar.length + yeni.length, say(noktalar, ([, , k]) => k), yeni.length); // 2) Ilkel cagri tek dosyada toplanmis: katmanin disina cikan nokta derlenmez. yaz(E, 'ilkel cagri tek dosyaya kapatilmis', noktalar.length + yeni.length, say(noktalar, ([, , k]) => k) + yeni.length, 0); console.log(`\nikinci duzende once kapatilmasi gereken nokta: ${say(noktalar, ([, , k]) => !k)}`);
islem turu nokta katmanda disarida kusurlu sifreleme 6 2 4 3 imza 4 3 1 0 karma 2 1 1 1 parola karmasi 2 1 1 1 toplam 14 7 7 5 disarida kalan noktalarin kusur dagilimi: etiketsiz kip 2 sabit baslangic degeri 1 kendi karma zinciri 1 tuzsuz karma 1 uc yeni cagri noktasi ekleniyor: duzen nokta katmanda yeni acik adayi katman var, dogrudan cagri serbest 17 7 3 ilkel cagri tek dosyaya kapatilmis 17 10 0 ikinci duzende once kapatilmasi gereken nokta: 7
Toplam satırı bu dersin üç sütununu veriyor: on dört nokta var, yedisini merkezî katman örtüyor, yedisi dışarıda kalıyor. Dördüncü sütun ise dışarıda kalmanın hemen açık anlamına gelmediğini söylüyor — yedi noktanın beşi kusurlu, ikisi doğru ayarla çağırıyor. O iki nokta bugün doğrudur çünkü onları yazan kişi ayarı bilerek seçmiştir; yarın da doğru kalacağını söyleyen hiçbir yapı yoktur. Kapsamanın ölçüsü şu anda kusurlu olan nokta sayısı değil, kusurlu olabilecek nokta sayısıdır ve o sayı yedidir.
Kusur dağılımı dört ayrı sınıf gösteriyor ve ikisi aynı kökten geliyor: bütünlük etiketi olmayan bir kip seçmek ve başlangıç değerini sabit bırakmak. Üçüncüsü kendi karma zincirini yazmaktır, dördüncüsü parolayı tuzsuz karmalamaktır. Aşağıdaki iki ölçüm önce üçüncü sınıfın, sonra ilk ikisinin somut karşılığını sayar.
Kendi Yazılan Karışımın Ölçüsü
Alım servisi yinelenen sayaç okumalarını ayırt etmek için elle yazılmış bir karışım işlevi kullanıyor: her karakterin kodu toplanıyor ve otuz iki bitlik bir değer üretiliyor. İşlev çalışıyor, sınamaları geçiyor ve hiçbir hata vermiyor.
// kripto/zincir.mjs — envanterdeki "kendi karma zinciri" kusurunun olculmesi: alim servisi // yinelenen okumayi ayirt etmek icin elle yazilmis bir karisim islevi kullaniyor (model). import { createHash } from 'node:crypto'; const KAYIT = 20000; const anahtarlar = Array.from({ length: KAYIT }, (_, i) => `SAY-${4000 + (i % 2500)}|2026-03-${String(1 + (i % 28)).padStart(2, '0')}T06:00Z|${41000 + i}`); // Elle yazilmis karisim: karakter kodlarinin toplami, 32 bitlik cikti. const kendiKarisim = (s) => { let h = 0; for (let i = 0; i < s.length; i++) h = (h + s.charCodeAt(i)) | 0; return h >>> 0; }; const hazirKarma = (s) => createHash('sha256').update(s).digest().readUInt32BE(0); const olc = (f) => { const gorulen = new Set(); let dusen = 0; for (const a of anahtarlar) { const k = f(a); if (gorulen.has(k)) dusen += 1; else gorulen.add(k); } return [gorulen.size, dusen]; }; const beklenen = KAYIT - 2 ** 32 * (1 - (1 - 1 / 2 ** 32) ** KAYIT); console.log(`${KAYIT} kurgu okuma anahtari, hepsi ayri, cikti genisligi 32 bit:`); console.log(` 32 bitte kuramsal beklenen sessiz dusme : ${beklenen.toFixed(2)}`); for (const [ad, f] of [['hazir karma', hazirKarma], ['elle yazilmis karisim', kendiKarisim]]) { const [ayri, dusen] = olc(f); console.log(` ${ad.padEnd(22)} ayri deger ${String(ayri).padStart(5)}, sessiz dusen ${dusen}`); }
20000 kurgu okuma anahtari, hepsi ayri, cikti genisligi 32 bit: 32 bitte kuramsal beklenen sessiz dusme : 0.05 hazir karma ayri deger 20000, sessiz dusen 0 elle yazilmis karisim ayri deger 67, sessiz dusen 19933
Otuz iki bitlik çıktı genişliğinde yirmi bin ayrı anahtarın çakışması kuramsal olarak 0,05 beklenir; hazır karmanın ilk otuz iki biti bu beklentiyi tutturuyor ve yirmi bin ayrı değer üretiyor. Elle yazılmış karışım aynı kümede altmış yedi ayrı değer üretiyor ve on dokuz bin dokuz yüz otuz üç okuma “zaten görülmüş” sayılarak sessizce düşüyor. Sebep açıktır: toplama işlemi karakterlerin sırasını da, kaydın uzunluğunu da yok sayar, dolayısıyla çıktı uzayının otuz iki bitlik genişliği hiçbir zaman kullanılmaz. Kendi algoritmasını yazmanın somut sonucu budur — yazılan şey çalışır, kimse hata görmez ve düşünülmemiş olan özellik yoktur.
Aynı Anahtar, Aynı Başlangıç Değeri
KI2 — akış biçiminde çalışan bir şifrelemede aynı anahtar ve aynı başlangıç değeri iki kez kullanılırsa, iki şifreli metnin bayt farkı iki düz metnin bayt farkına eşit olur. Bu, bir algoritma kusuru değildir; yapının kullanım koşulunun çiğnenmesidir. Ölçüm kendi yazdığımız model üzerinde yapılır: aşağıdaki akış üreteci gerçek bir şifre değildir ve üretim kodunda kendi şifresini yazan bir yaklaşımı temsil eder.
// kripto/model.mjs — kendi yazdigimiz basit akis modeli ile hazir kimlik dogrulamali // sifrelemenin ayni olcekte karsilastirilmasi. Model gercek bir sifre degildir; yalniz // baslangic degeri yeniden kullanildiginda ve butunluk etiketi olmadiginda sizan bilgiyi // saymak icin yazilmistir. Kurgu bolgesel olcum agindan iki sayac okumasi kullanilir. import { createHash, createCipheriv, createDecipheriv } from 'node:crypto'; const ANAHTAR = createHash('sha256').update('kurgu-saha-anahtari').digest(); const OKUMA_A = Buffer.from('SAY-4417|2026-03-01T06:00Z|41982|sube-01'); const OKUMA_B = Buffer.from('SAY-4418|2026-03-01T06:00Z|41982|sube-02'); const BICIM = /^SAY-\d{4}\|\d{4}-\d{2}-\d{2}T\d{2}:\d{2}Z\|\d+\|sube-\d{2}$/; // Kendi akis uretecimiz: anahtar ve baslangic degerinden bayt dizisi turetir. function akis(bd, uzunluk) { const parcalar = []; for (let blok = 0; parcalar.length * 32 < uzunluk; blok++) { parcalar.push(createHash('sha256').update(ANAHTAR).update(bd).update(String(blok)).digest()); } return Buffer.concat(parcalar).subarray(0, uzunluk); } const modelSifrele = (duz, bd) => Buffer.from(duz.map((b, i) => b ^ akis(bd, duz.length)[i])); const modelCoz = (sifreli, bd) => Buffer.from(sifreli.map((b, i) => b ^ akis(bd, sifreli.length)[i])); // Hazir yapi: kimlik dogrulamali sifreleme (AES-256-GCM); etiket paketle tasinir. const aeSifrele = (duz, bd) => { const s = createCipheriv('aes-256-gcm', ANAHTAR, bd); return { govde: Buffer.concat([s.update(duz), s.final()]), etiket: s.getAuthTag() }; }; const aeCoz = ({ govde, etiket }, bd) => { const c = createDecipheriv('aes-256-gcm', ANAHTAR, bd); c.setAuthTag(etiket); return Buffer.concat([c.update(govde), c.final()]); }; const BD1 = Buffer.alloc(12, 7), BD2 = Buffer.alloc(12, 9); const esitSayisi = (x, y) => [...x].filter((b, i) => b === y[i]).length; const yaz = (g, ...s) => console.log(s.map((v, i) => (g[i] < 0 ? String(v).padEnd(-g[i]) : String(v).padStart(g[i]))).join('')); // 1) Ayni anahtarla ayni baslangic degeri: iki sifreli metin karsilastiriliyor. console.log(`KI2 — ayni anahtar, iki kayit, uzunluk ${OKUMA_A.length} bayt,` + ` duz metinde esit konum ${esitSayisi(OKUMA_A, OKUMA_B)}:`); const D = [-32, 14, 14, 14]; yaz(D, 'duzen', 'esit sifreli', 'fark okunur', 'sizan konum'); for (const [ad, c1, c2] of [ ['model, ayni baslangic degeri', modelSifrele(OKUMA_A, BD1), modelSifrele(OKUMA_B, BD1)], ['model, ayri baslangic degeri', modelSifrele(OKUMA_A, BD1), modelSifrele(OKUMA_B, BD2)], ['etiketli, ayri baslangic degeri', aeSifrele(OKUMA_A, BD1).govde, aeSifrele(OKUMA_B, BD2).govde], ]) { const okunur = [...c1].every((b, i) => (b ^ c2[i]) === (OKUMA_A[i] ^ OKUMA_B[i])); yaz(D, ad, esitSayisi(c1, c2), okunur ? 'evet' : 'hayir', okunur ? OKUMA_A.length : 0); } // 2) Butunluk etiketi olmayan sifreleme: degistirilen paketi kim yakaliyor. const DENEME = 24; console.log(`\nKI3 — ${DENEME} paketin her birinde tek bit cevriliyor:`); const E = [-32, 14, 14, 14]; yaz(E, 'duzen', 'bicim gecen', 'yakalanan', 'degisen bayt'); let gecen = 0, degisen = 0, aGecen = 0; for (let k = 0; k < DENEME; k++) { const bd = Buffer.alloc(12, k + 16); const mc = Buffer.from(modelSifrele(OKUMA_A, bd)); mc[k] ^= 1 << (k % 8); const cozulen = modelCoz(mc, bd); degisen += [...cozulen].filter((b, i) => b !== OKUMA_A[i]).length; if (BICIM.test(cozulen.toString('latin1'))) gecen += 1; const paket = aeSifrele(OKUMA_A, bd); paket.govde[k] ^= 1 << (k % 8); try { aeCoz(paket, bd); aGecen += 1; } catch { /* etiket reddetti */ } } yaz(E, 'model + bicim denetimi', gecen, DENEME - gecen, degisen); yaz(E, 'kimlik dogrulamali sifreleme', aGecen, DENEME - aGecen, 0);
KI2 — ayni anahtar, iki kayit, uzunluk 40 bayt, duz metinde esit konum 38: duzen esit sifreli fark okunur sizan konum model, ayni baslangic degeri 38 evet 40 model, ayri baslangic degeri 0 hayir 0 etiketli, ayri baslangic degeri 0 hayir 0 KI3 — 24 paketin her birinde tek bit cevriliyor: duzen bicim gecen yakalanan degisen bayt model + bicim denetimi 4 20 24 kimlik dogrulamali sifreleme 0 24 0
Birinci tablonun ilk satırı sızıntıyı sayıyla veriyor. İki sayaç okuması kırk baytlık kayıtlardır ve düz metinde otuz sekiz konumda birbirine eşittir — ikisi de aynı tarihi, aynı okuma değerini ve aynı biçimi taşır, yalnızca sayaç numarası ile şube kodunda ayrılır. Aynı başlangıç değeriyle şifrelendiklerinde şifreli metinler de tam olarak otuz sekiz konumda eşit çıkıyor. Şifreli metne bakan biri, iki kaydın nerede aynı nerede farklı olduğunu anahtarı hiç bilmeden okuyor.
“Fark okunur” sütunu daha keskin bir şey söylüyor: kırk konumun tamamında, iki şifreli metnin bayt farkı iki düz metnin bayt farkına eşit. Şifreleme kaydın değerini değil yalnızca ham hâlini gizler; iki kayıt arasındaki bütün ilişki şifreli tarafa aynen geçer. Bir okuma kümesinde bunun anlamı, hangi kayıtların özdeş olduğunun ve bir şubenin okumalarının ne zaman durduğunun sayılabilmesidir. İkinci ve üçüncü satır aynı ölçüde sıfır veriyor: başlangıç değeri her şifreleme için ayrı olduğunda eşit konum sıfır, okunur fark yok. Kusur şifrelemede değil, tek bir parametrenin sabit bırakılmasındadır.
Değişikliği Fark Etmeyen Şifreleme
KI3 — bütünlük etiketi olmayan bir şifreleme, çözmeyi reddedecek bir kaleme sahip değildir; her şifreli metin bir düz metne çözülür. İkinci tablo bu cümlenin kapsama karşılığını veriyor. Yirmi dört pakette birer bit çevrildi; her çevirme çözülen düz metinde tam olarak bir bayt değiştirdi — toplam yirmi dört bayt, paket başına bir. Akış biçiminde şifreli metnin bir konumundaki değişim düz metnin aynı konumuna düşer, ne yayılır ne de fark edilir.
Bu noktada tek savunma alıcı tarafın biçim denetimidir ve tablo onun kapsamasını sayıyor: yirmi dört değişikliğin yirmisini yakalıyor, dördünü kaçırıyor. Kaçırılan dördü, düz metnin biçim kalıbına uyan konumlarına düşenlerdir — sayaç numarasının ya da okuma değerinin rakamları. Sonuç, kaydın hâlâ geçerli göründüğü ama taşıdığı değerin başka olduğu bir pakettir; faturalama tarafında bunun karşılığı sessizce yanlış bir tüketimdir. Bir savunma noktaların çoğunu örtebilir ve yine de örtmediği noktalar tam olarak önemli olanlar olabilir.
Son satır aynı ölçünün öteki ucudur: kimlik doğrulamalı şifrelemede yirmi dört değişikliğin yirmi dördü reddedildi ve biçim denetimine iş düşmedi. Buradaki fark bir güç farkı değil, kapsama farkıdır — bütünlük denetimi paketin tamamını, biçim denetimi yalnızca kalıba giren alanları örter.
Yeni Nokta Eklendiğinde
Üçüncü tablo kursun üçüncü sorusunu soruyor. Kod tabanına üç yeni çağrı noktası ekleniyor: saha uygulamasının çevrimdışı kuyruğu, portalın dışa aktarma akışı, alım servisinin ölçüm arşivi. İlk düzende merkezî katman vardır ama ilkel çağrı serbesttir. Nokta sayısı on dörtten on yediye çıkıyor, katmandaki nokta sayısı yedide kalıyor ve üç yeni nokta doğrudan açık adayı oluyor. Katman kimseyi bağlamadığı için yeni nokta onu kullanmak zorunda değildir; kapsama sabit kalmaz, her eklemede düşer.
İkinci düzende ilkel çağrılar tek bir dosyada toplanmıştır ve o dosyanın dışındaki hiçbir yer şifreleme, imza ya da karma ilkelini çağıramaz. Üç yeni nokta katmandan geçmek zorundadır; katman sayısı ona çıkar, yeni açık adayı sıfırdır. Kapsamayı sabit tutan şey katmanın var olması değil, katmanın dışına çıkmanın mümkün olmamasıdır. Bedeli son satırda yazılı: bu düzene geçmeden önce dışarıda kalan yedi noktanın yedisini de taşımak gerekir, çünkü tek dosya kuralı ancak istisnası olmadığında iş görür. Yedi noktalık bir borç, üç yeni noktanın her birinde tekrarlanacak bir kararın yerini alır.
Özet
- Kurgu ölçüm ağının kod tabanında on dört kriptografi çağrı noktası var; yedisi merkezî katmandan geçiyor, yedisi dışarıda kalıyor ve dışarıdakilerin beşi kusurlu ayarla çağırıyor. Kapsamanın ölçüsü kusurlu nokta sayısı değil, kusurlu olabilecek nokta sayısıdır.
- Elle yazılmış toplama karışımı yirmi bin ayrı anahtarda altmış yedi ayrı değer üretti ve 19.933 okuma sessizce düştü; hazır karmanın ilk otuz iki biti kuramsal beklentiyle uyumlu, sıfır düşme verdi.
- Aynı anahtarla aynı başlangıç değeri kullanıldığında iki kırk baytlık kaydın şifreli metinleri otuz sekiz konumda eşit çıkıyor ve kırk konumun tamamında fark doğrudan okunuyor; ayrı başlangıç değerinde bu sayılar sıfır.
- Bütünlük etiketi olmayan şifrelemede tek bit çevirmek düz metinde tam bir bayt değiştiriyor; biçim denetimi yirmi dört değişikliğin yirmisini yakalıyor, dördünü kaçırıyor. Kimlik doğrulamalı şifreleme aynı yirmi dördün yirmi dördünü reddediyor.
- İlkel çağrı serbestken üç yeni nokta üç açık adayı ekliyor ve kapsama düşüyor; ilkel çağrı tek dosyaya kapatıldığında kapsama sabit kalıyor, bedeli önce yedi noktanın taşınmasıdır.
Sonraki Adım
Bu dersteki bütün ölçümler bir şeyi verilmiş saydı: anahtarı ve başlangıç değerini üreten kaynağın tahmin edilemez olduğunu. Oysa o kaynak da kod tabanında bir çağrı noktasıdır ve on dört noktalık envanterde ayrı bir satır olarak görünmez. Saha uygulamasının yükleme belirtecini, portalın parola sıfırlama bağlantısını ve fatura dosya adını üreten çağrılar aynı soruyu paylaşır: üretilen değeri üçüncü bir taraf önceden hesaplayabilir mi? Sonraki ders bu soruyu iki üreteci aynı ölçüde koşturarak yanıtlar — tohum bilindiğinde kaç değer önceden çıkıyor ve belirteç üreten noktaların kaçı zayıf üreteci çağırıyor.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.