İçeriğe geç
academia.sh

Ders 11 / 21

TLS El Sıkışması

El sıkışma kimlik adımları ekseninde sökülür: altı iddianın her biri tek bir adıma bağlı, dört kimlik adımından herhangi biri kapatıldığında beş kurgu sunuştan tam olarak biri daha kabul ediliyor ve hiçbir kimlik adımı yokken beşinin beşi geçiyor; tam doğrulama 11 kalemin aynı anda tutmasını, karşılıklı doğrulama 15'ini istiyor ve filo ölçeğinde 17.552 kalem ekliyor.

İçindekiler

Önceki üç ders sertifikanın alanlarını, zincirini ve iptal durumunu ayrı ayrı ölçtü. Bu denetimlerin hiçbiri kendi başına yapılmaz; hepsi tek bir konuşmanın içinde, bağlantı kurulurken ve saniyenin altında bir sürede toplanır. O konuşma el sıkışmadır.

El sıkışmaya çoğunlukla “şifreli kanalın kurulduğu yer” denir. Bu tanım eksiktir: şifreli kanal kimlikten bağımsız olarak kurulabilir ve kurulduğunda ortaya kiminle konuşulduğu bilinmeyen gizli bir kanal çıkar. Bu ders el sıkışmayı şifreleme tarafından değil kimlik tarafından söker ve üç şey ölçer: hangi adım hangi iddiayı kanıtlıyor, bir adım atlandığında hangi iddia kanıtsız kalıyor ve kaç sunuş geçiyor, istemcinin de kalem sunması kaç kalem ekliyor. Yapılandırma kalemleri ve yanıt başlıkları bu dersin dışındadır; onlar Backend Güvenliği ve Bulut Güvenliği kurslarında ölçüldü.

  • PK16. El sıkışma dört kimlik adımı olarak modellenir: zincir doğrulaması, ad denetimi, iptal denetimi, kalem sahipliği imzası. Kalemler kurgudur; imza üretimi, anahtar değişimi ve özet doğrulaması node:crypto ile gerçekten koşar.
  • PK17. Kalem sahipliği imzası, karşı tarafın el sıkışma özetini sertifikadaki açık anahtarın eşiyle imzalamasıdır. Sertifika herkese açık olarak sunulur; imzayı üretebilmek özel anahtarı gerektirir.
  • PK18. Geçici anahtarlar her el sıkışmada yeniden üretilir ve ortak sır bu geçici çiftlerden türetilir; sertifikanın anahtarı ortak sırra girmez.
  • PK19. Sunuş denemesi, karşı tarafın bir kalem sunması ve özeti bir anahtarla imzalamasıdır. Beş kurgu sunuşun biri meşrudur.
  • PK20. Bir ayarda kapatılan adım hiç çalıştırılmaz; o adımın kanıtladığı iddia kanıtsız sayılır ve o adımın kalemleri sayımdan düşer.

Adımlar ve Kanıtladıkları

Her kimlik adımı ayrı bir cümle kurar. Aşağıdaki koşum bu cümleleri adımlara eşliyor ve her adımın tutmasını gerektirdiği kalemleri sayıyor.

// adim.mjs — el sikismanin kimlik adimlari; kalemler kurgu, imza ve anahtar uretimi gercek.
import { generateKeyPairSync, sign, verify, diffieHellman, createHmac } from "node:crypto";

const ed = () => generateKeyPairSync("ed25519");
export const KOK = ed(), ARA = ed(), YABANCI_ARA = ed();
const SUNUCU = ed(), FATURA = ed(), YABANCI_UC = ed(), IPTALLI = ed(), ISTEMCI = ed();

let sira = 7300;
const kayit = (ozne, altAd, veren, gizli, cift, otoriteMi = false) => {
  const k = { ozne, altAd, veren, seri: sira++, otoriteMi,
    acik: cift.publicKey.export({ type: "spki", format: "der" }).toString("hex") };
  k.imza = sign(null, govde(k), gizli);
  return k;
};
export const govde = (k) => Buffer.from(JSON.stringify([k.ozne, k.altAd, k.veren, k.seri,
  k.otoriteMi, k.acik]));

export const ARA_KALEM = kayit("olcum-ara", ["olcum-ara"], "kurum-koku",
  KOK.privateKey, ARA, true);
const YABANCI_KALEM = kayit("yabanci-ara", ["yabanci-ara"], "yabanci-kok",
  YABANCI_ARA.privateKey, YABANCI_ARA, true);
export const KALEM = {
  dogru: kayit("olcum-toplayici", ["olcum-toplayici"], "olcum-ara", ARA.privateKey, SUNUCU),
  fatura: kayit("fatura-servisi", ["fatura-servisi"], "olcum-ara", ARA.privateKey, FATURA),
  yabanci: kayit("olcum-toplayici", ["olcum-toplayici"], "yabanci-ara",
    YABANCI_ARA.privateKey, YABANCI_UC),
  iptalli: kayit("olcum-toplayici", ["olcum-toplayici"], "olcum-ara", ARA.privateKey, IPTALLI),
  istemci: kayit("saha-terminali-17", ["saha-terminali-17"], "olcum-ara", ARA.privateKey,
    ISTEMCI),
};
export const GIZLI = { dogru: SUNUCU.privateKey, fatura: FATURA.privateKey,
  yabanci: YABANCI_UC.privateKey, iptalli: IPTALLI.privateKey, istemci: ISTEMCI.privateKey };
export const IPTAL_LISTESI = [KALEM.iptalli.seri];
export const ARA_KAYDI = { "olcum-ara": ARA_KALEM, "yabanci-ara": YABANCI_KALEM };

// Bir el sikisma kosumu: gecici anahtarlar uretilir, ortak sir turetilir, ozet imzalanir.
export function elSikis(sunus, hedefAd, ayar) {
  const iGecici = generateKeyPairSync("x25519"), sGecici = generateKeyPairSync("x25519");
  const ozet = Buffer.concat([Buffer.from("istemci-selami|sunucu-selami|"),
    iGecici.publicKey.export({ type: "spki", format: "der" }),
    sGecici.publicKey.export({ type: "spki", format: "der" })]);
  const sunulan = KALEM[sunus.kalem];
  const sahiplikImzasi = sign(null, ozet, GIZLI[sunus.imzalayan]);

  const sonuc = { zincir: true, ad: true, iptal: true, sahiplik: true };
  if (ayar.zincir) {
    const ara = ARA_KAYDI[sunulan.veren];
    sonuc.zincir = Boolean(ara) && verify(null, govde(ara), KOK.publicKey, ara.imza) &&
      ara.otoriteMi && verify(null, govde(sunulan), ARA.publicKey, sunulan.imza);
  }
  if (ayar.ad) sonuc.ad = sunulan.altAd.includes(hedefAd);
  if (ayar.iptal) sonuc.iptal = !IPTAL_LISTESI.includes(sunulan.seri);
  if (ayar.sahiplik) {
    const acik = { key: Buffer.from(sunulan.acik, "hex"), format: "der", type: "spki" };
    sonuc.sahiplik = verify(null, ozet, acik, sahiplikImzasi);
  }
  const kabul = Object.values(sonuc).every(Boolean);
  // Ortak sir yalnizca kabul edilen el sikismada turetilir.
  let esitAnahtar = false, bitis = false;
  if (kabul) {
    const a = diffieHellman({ privateKey: iGecici.privateKey, publicKey: sGecici.publicKey });
    const b = diffieHellman({ privateKey: sGecici.privateKey, publicKey: iGecici.publicKey });
    esitAnahtar = a.equals(b);
    bitis = createHmac("sha256", a).update(ozet).digest()
      .equals(createHmac("sha256", b).update(ozet).digest());
  }
  return { ...sonuc, kabul, esitAnahtar, bitis };
}

export const yaz = (g) => (h) =>
  h.map((v, i) => (i ? String(v).padStart(g[i]) : String(v).padEnd(g[0]))).join("");
// olcum.mjs — adim basina kanitlanan iddia, atlanan adimin actigi yol, karsilikli dogrulama
import { elSikis, yaz } from "./adim.mjs";

// Hangi adim hangi iddiayi kanitliyor.
export const IDDIA = [
  ["sunulan kalem tanidik bir koke baglaniyor", "zincir"],
  ["kalem konusulan ad icin duzenlenmis", "ad"],
  ["kalem iptal edilmemis", "iptal"],
  ["karsi taraf kalemin ozel anahtarini elinde tutuyor", "sahiplik"],
  ["oturum anahtari iki tarafta ayni ve ucuncu tarafta yok", "anahtar"],
  ["adim ozeti iki tarafta ayni", "bitis"],
];
// Her adimin tutmasini gerektirdigi kalemler.
const KALEMLER = {
  zincir: ["kok deposu kaydi", "ara kalemin gecerliligi", "ara kalemin ozel anahtari"],
  ad: ["kalemin ad listesi", "istemcinin hedef adi"],
  iptal: ["iptal kaynagi guncel", "iptal kaynagina erisim"],
  sahiplik: ["sunucunun ozel anahtari gizli"],
  anahtar: ["gecici anahtarin her el sikismada tekil olmasi"],
  bitis: ["ozetin iki tarafta ayni sirayla uretilmesi"],
};
const SABIT = ["dogrulayanin saati"];
const ISTEMCI_KALEMLERI = ["istemci kaleminin zinciri", "istemci kaleminin iptal durumu",
  "istemcinin ozel anahtari gizli", "istemci kaleminin dagitimi"];

// Kurgu sunus denemeleri: hangi kalem sunuluyor, ozeti hangi anahtar imzaliyor.
const SUNUS = [
  ["dogru kalem, dogru ad", { kalem: "dogru", imzalayan: "dogru" }, "olcum-toplayici", "mesru"],
  ["baska ad icin duzenlenmis kalem", { kalem: "fatura", imzalayan: "fatura" },
    "olcum-toplayici", "ad"],
  ["bilinmeyen otoritenin kalemi", { kalem: "yabanci", imzalayan: "yabanci" },
    "olcum-toplayici", "zincir"],
  ["iptal edilmis kalem", { kalem: "iptalli", imzalayan: "iptalli" }, "olcum-toplayici", "iptal"],
  ["kopyalanmis kalem, ozel anahtar yok", { kalem: "dogru", imzalayan: "istemci" },
    "olcum-toplayici", "sahiplik"],
];

const AYAR = [
  ["tam dogrulama", { zincir: 1, ad: 1, iptal: 1, sahiplik: 1 }, false],
  ["ad denetimi kapali", { zincir: 1, ad: 0, iptal: 1, sahiplik: 1 }, false],
  ["zincir dogrulamasi kapali", { zincir: 0, ad: 1, iptal: 1, sahiplik: 1 }, false],
  ["iptal denetimi kapali", { zincir: 1, ad: 1, iptal: 0, sahiplik: 1 }, false],
  ["sahiplik imzasi atlanmis", { zincir: 1, ad: 1, iptal: 1, sahiplik: 0 }, false],
  ["hicbir kimlik adimi yok", { zincir: 0, ad: 0, iptal: 0, sahiplik: 0 }, false],
  ["tam dogrulama + karsilikli", { zincir: 1, ad: 1, iptal: 1, sahiplik: 1 }, true],
];

const ilk = elSikis(SUNUS[0][1], SUNUS[0][2], { zincir: 1, ad: 1, iptal: 1, sahiplik: 1 });
console.log(`tam dogrulamada mesru el sikisma: kabul ${ilk.kabul}, ` +
  `ortak sir esit ${ilk.esitAnahtar}, bitis dogrulamasi ${ilk.bitis}`);
console.log(`${SUNUS.length} kurgu sunus denemesi, ${AYAR.length} ayar\n`);

const y1 = yaz([54, 18, 8]);
console.log(y1(["iddia", "kanitlayan adim", "kalem"]));
for (const [ad, adim] of IDDIA)
  console.log(y1([ad, adim, (KALEMLER[adim] ?? []).length]));

const y2 = yaz([30, 12, 10, 10, 16, 8]);
console.log("\n" + y2(["ayar", "kanitlanan", "kanitsiz", "kabul/5", "kanitsiz gecen", "kalem"]));
for (const [ad, ayar, karsilikli] of AYAR) {
  const sonuc = SUNUS.map(([, s, hedef]) => elSikis(s, hedef, ayar));
  const kabul = sonuc.filter((r) => r.kabul).length;
  const kanitlanan = IDDIA.filter(([, a]) => ayar[a] === undefined || ayar[a]).length +
    (karsilikli ? 1 : 0);
  const kalem = [...Object.entries(KALEMLER).filter(([a]) => ayar[a] === undefined || ayar[a])
    .flatMap(([, l]) => l), ...SABIT, ...(karsilikli ? ISTEMCI_KALEMLERI : [])].length;
  const toplamIddia = IDDIA.length + (karsilikli ? 1 : 0);
  console.log(y2([ad, kanitlanan, toplamIddia - kanitlanan, kabul, kabul - 1, kalem]));
}

console.log(`\nkarsilikli dogrulamanin ekledigi iddia: istemci kurumun tanidigi bir ozne`);
console.log(`ekledigi kalem (baglanti basina ${ISTEMCI_KALEMLERI.length}):`);
for (const k of ISTEMCI_KALEMLERI) console.log(`  ${k}`);
const KARSILIKLI = elSikis({ kalem: "istemci", imzalayan: "istemci" }, "saha-terminali-17",
  { zincir: 1, ad: 1, iptal: 1, sahiplik: 1 });
console.log(`istemci yonunde ayni dort adim kosturuldu: kabul ${KARSILIKLI.kabul}`);
const FILO = [["sayac ucu", 4100], ["ic servis", 26], ["personel is istasyonu", 262]];
const toplam = FILO.reduce((t, [, n]) => t + n, 0);
console.log(`filo olceginde eklenen kalem: ${toplam} uc kalem x ` +
  `${ISTEMCI_KALEMLERI.length} = ${toplam * ISTEMCI_KALEMLERI.length}`);
tam dogrulamada mesru el sikisma: kabul true, ortak sir esit true, bitis dogrulamasi true
5 kurgu sunus denemesi, 7 ayar

iddia                                                    kanitlayan adim   kalem
sunulan kalem tanidik bir koke baglaniyor                         zincir       3
kalem konusulan ad icin duzenlenmis                                   ad       2
kalem iptal edilmemis                                              iptal       2
karsi taraf kalemin ozel anahtarini elinde tutuyor              sahiplik       1
oturum anahtari iki tarafta ayni ve ucuncu tarafta yok           anahtar       1
adim ozeti iki tarafta ayni                                        bitis       1

ayar                            kanitlanan  kanitsiz   kabul/5  kanitsiz gecen   kalem
tam dogrulama                            6         0         1               0      11
ad denetimi kapali                       5         1         2               1       9
zincir dogrulamasi kapali                5         1         2               1       8
iptal denetimi kapali                    5         1         2               1       9
sahiplik imzasi atlanmis                 5         1         2               1      10
hicbir kimlik adimi yok                  2         4         5               4       3
tam dogrulama + karsilikli               7         0         1               0      15

karsilikli dogrulamanin ekledigi iddia: istemci kurumun tanidigi bir ozne
ekledigi kalem (baglanti basina 4):
  istemci kaleminin zinciri
  istemci kaleminin iptal durumu
  istemcinin ozel anahtari gizli
  istemci kaleminin dagitimi
istemci yonunde ayni dort adim kosturuldu: kabul true
filo olceginde eklenen kalem: 4388 uc kalem x 4 = 17552

İlk satır meşru el sıkışmanın gerçekten koştuğunu söylüyor: kalem kabul edildi, iki tarafın türettiği ortak sır birbirine eşit ve özet üzerinde hesaplanan bitiş değeri iki tarafta aynı çıktı. Bu üç sonuç ayrı ayrı doğrulandı, çünkü üçü ayrı iddiaya karşılık gelir.

İddia tablosu adımların iş bölümünü gösteriyor. Zincir doğrulaması kalemin tanıdık bir köke bağlandığını söyler ama kalemin kimi tanımladığını söylemez. Ad denetimi kalemin konuşulan ad için düzenlendiğini söyler ama kalemin geçerli olup olmadığını söylemez. Kalem sahipliği imzası ise ötekilerin hiçbirinin söylemediği şeyi söyler: karşı taraf bu kalemi yalnız göstermiyor, özel anahtarını da elinde tutuyor. Sertifika herkese açık olarak sunulan bir belgedir; onu kopyalamak hiçbir engel gerektirmez. Sahiplik imzasını üretmek ise sertifikanın kendisinde bulunmayan bir şeyi gerektirir.

Son iki iddia kimlik adımlarından bağımsızdır. Ortak sırrın iki tarafta aynı olması ve özetin değişmemiş olması, hiçbir kalem sunulmasa bile sağlanır. Bu, dersin en çok karıştırılan noktasıdır ve son ayar satırında görünür.

Bir Adım Atlandığında

İkinci tablo dört ayarı yan yana koyuyor ve her birinde tek bir adım kapatılıyor. Dördünde de aynı şey oluyor: kanıtlanan iddia altıdan beşe iniyor ve kabul edilen sunuş birden ikiye çıkıyor. Yani her adım tam olarak bir sunuşu durduruyor ve hiçbiri bir ötekinin yedeği değil. Ad denetimi kapatıldığında fatura servisi için düzenlenmiş kalem toplayıcı yerine geçiyor; zincir doğrulaması kapatıldığında bilinmeyen bir otoritenin ürettiği kalem geçiyor; iptal denetimi kapatıldığında geçersiz sayılmış kalem geçiyor.

Beşinci satır bu dördünden ayrılıyor. Sahiplik imzası atlandığında tutması gereken kalem sayısı on birden ona iniyor — bütün ayarlar içinde en küçük düşüş. Buna karşılık geçen sunuş, kalemin bir kopyasını taşıyan ve özel anahtarı hiç olmayan bir taraftır. En az kalem tasarrufu sağlayan atlama, savunma açısından en pahalı olanıdır; çünkü kaldırdığı tek kalem, sertifikanın taşımadığı tek şeydir.

Altıncı satır ölçünün alt sınırını veriyor. Hiçbir kimlik adımı yokken beş sunuşun beşi de kabul ediliyor, kanıtsız geçen dört, tutması gereken kalem üç. Fakat kanıtlanan iddia sayısı sıfır değil, iki: ortak sır yine iki tarafta aynı ve özet yine değişmemiş. Bu ayarda bağlantı şifrelidir ve dinleyen bir üçüncü taraf içeriği okuyamaz — yalnız konuşulan tarafın kim olduğu bilinmez. Şifreleme ile kimlik doğrulama iki ayrı iştir; ilki ikincisi olmadan da tamamlanır ve tamamlandığı anda ortada başarılı görünen bir bağlantı vardır.

Dört adımın hiçbirinin söylemediği bir şey de var. Ad denetimi, kalemi istemcinin gitmek istediği adla karşılaştırır; o adın doğru olup olmadığını sormaz. İstemci yanlış bir ada yöneltilmişse ve karşı taraf o ad için geçerli bir kalem taşıyorsa dört adımın dördü de geçer. Zincir doğrulaması da benzer bir sınır taşır: kalemin tanıdık bir köke bağlandığını söyler, o kalemin arkasındaki tarafın güvenilir olduğunu değil.

Karşılıklı Doğrulamanın Eklediği

Şimdiye kadar ölçülen her şey tek yönlüydü: istemci sunucunun kim olduğunu soruyordu, sunucu istemciye aynı soruyu sormuyordu. Son ayar bu soruyu ekliyor ve aynı dört adım ters yönde koşuyor — koşum bunu ayrıca doğruluyor, istemci kalemi de kabul ediliyor.

Kazanç bir iddiadır: istemci, kurumun tanıdığı bir öznedir. Bedeli bağlantı başına dört kalem ve toplam on beş. Bu dört kalemin üçü sunucu tarafındakilerin aynadaki eşidir; dördüncüsü yenidir ve asıl yükü taşır: istemci kaleminin dağıtımı. Sunucu tarafında bir kalem vardır ve bir yerde durur; istemci tarafında kalem, bağlanan her makineye ayrı ayrı yerleştirilmelidir.

Filo ölçeğinde bu 4.388 uç kalem için 17.552 kalem demektir. Sayı, ikinci dersin envanteriyle birlikte okunduğunda anlam kazanır: karşılıklı doğrulama, kimlik doğrulama tarafında bir iddia eklerken yaşam döngüsü tarafında dört bin üç yüz seksen sekiz kalemlik bir yük açar. Bu yükün kendisi bir savunma değildir; savunmanın sürdürülebilmesinin koşuludur ve ödenmediğinde eklenen iddia da kanıtsız kalır.

Bu, dört adımın hepsi için geçerli bir sonuca işaret ediyor. El sıkışma anında ölçülen her iddia, bağlantıdan çok önce yapılmış bir işe dayanır: kalemin düzenlenmesine, adının doğru yazılmasına, köke ulaştırılmasına ve süresi dolmadan yenilenmesine. Ölçüm bağlantı anında yapılır, iş ise başka bir zamanda.

Özet

  • El sıkışmanın dört kimlik adımı altı iddiadan dördünü kanıtlar; kalan iki iddia (ortak sırrın eşitliği ve özetin değişmemişliği) kimlik adımlarından bağımsız olarak sağlanır.
  • Dört adımın herhangi biri kapatıldığında kanıtlanan iddia 6’dan 5’e, kabul edilen sunuş 1’den 2’ye gider: her adım tam olarak bir sunuşu durdurur ve hiçbiri bir ötekinin yedeği değildir.
  • Sahiplik imzasını atlamak tutması gereken kalemi yalnız 1 azaltır (11’den 10’a) ama sertifikanın bir kopyasını taşıyan, özel anahtarı olmayan tarafı kabul eder. En küçük tasarruf, en pahalı açıklıktır.
  • Hiçbir kimlik adımı yokken 5 sunuşun 5’i geçiyor, ama 2 iddia hâlâ kanıtlı: bağlantı şifrelidir ve içeriği okunamaz — yalnız karşı tarafın kim olduğu bilinmez. Şifreleme, kimlik doğrulama olmadan da tamamlanır.
  • Karşılıklı doğrulama 1 iddia ekler, bağlantı başına kalemi 11’den 15’e çıkarır ve filo ölçeğinde 4.388 uç kalem için 17.552 kalem yük getirir; bu yükün büyük kısmı doğrulama değil, dağıtım ve yaşam döngüsü işidir.

Sonraki Adım

Bu dersin son cümlesi bir sonrakinin sorusudur. El sıkışmada ölçülen bütün iddialar, bağlantıdan önce yapılmış bir işin sonucuydu ve o işin en kırılgan parçası zamanla ilgilidir: her kalemin bir geçerlilik penceresi vardır ve pencere dolduğunda kalem, hiçbir şey değişmemiş olsa bile geçmeyi bırakır. Sonraki ders bu pencereyi ölçer — yenileme elle yapıldığında kaç kalem gözden kaçıyor, süresi dolmuş tek bir kalem kaç bağlantı düşürüyor, otomasyon hangi kalemi kapatıyor ve kalem ömrünü kısaltmak kaç kalem bakım işi ekliyor.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat