Bölüm 10:TLS El Sıkışması
Ağlar'a “Sen”i Katmak yazısının parçası: adres çubuğuna yazdığın tek bir isteğin peşinden bakırla fiberin fiziğine kadar inen uzun bir teknik yolculuk.
Tüm bölümler
- Giriş
- Adres Çubuğundan İsteğe
- Paket Anahtarlamalı Yol
- İsimden Adrese
- Önbellek, Tazelik ve Güven
- Port Numarası ve Soket
- Bağlantısız Taşıma: UDP
- Üç Adımlı El Sıkışma
- Simetrik Şifreleme
- Açık Anahtarlı Kriptografi
- TLS El Sıkışması
- Sertifika Zinciri
- HTTP Mesajının Anatomisi
- Durum, Çerez ve Oturum
- HTTP/2'den HTTP/3'e
- İçerik Dağıtım Ağları
- Diğer Uygulama Protokolleri
- Katmanlı Mimari
- Kapsülleme ve Başlıklar
- Paket Yakalama ve Çözümleme
- Ağ Arayüz Kartı
- Fiziksel Adresleme
- Hata Sezme ve CRC
- Adres Çözümleme: ARP
- Anahtarlama ve Ortam Erişimi
- Ağ Topolojileri
- Sanal Yerel Ağlar
- Kablosuz Erişim: 802.11
- Radyo Kanalı ve Kapasite
- Kablosuz Ağ Güvenliği
- Hücresel Ağlar
- Spektrum ve Kuşaklar
- Hareketlilik ve Aktarma
- Adres Kiralama: DHCP
- Adresin Anatomisi
- Adres Tükenmesi ve IPv6
- Yönlendirici Mimarisi
- Kuyruk Yönetimi
- Cihaz Yapılandırma
- Yönlendirme Protokolleri
- Kontrol Düzlemi ve SDN
- Ağ İzleme ve Yönetimi
- Alanlar Arası Yönlendirme
- Adres Çevirisi
- Ara Kutular
- Tünelleme ve IPsec
- Güvenlik Duvarları
- Operatör ve Metro Ağları
- Bitten Sinyale
- Çoklama Teknikleri
- İletim Ortamları
- Kablo Sonlandırma
- Fiber Optik İletim
- Bakır Erişim Ağı: DSL
- Pasif Optik Ağlar
- Kablo Erişim Ağları
- Denizaltı Kabloları
- Veri Merkezi Ağları
- Kapsülleme Çözme
- Güvenilir Aktarım
- Tıkanıklık Denetimi
- Trafik Çözümleme
- Gecikme Bütçesi
- Son Söz
Simetrik şifreleme, anahtar uzlaşması ve imza ayrı ayrı elinde. Üçünü tek bir kanalda birleştiren şey bir bağlantı.
Kilit o kanalda kuruluyor ve birkaç mesaja sığıyor. Peki el sıkışma tam olarak neyi pazarlıyor?
Kilit tam olarak nereye takılıyor?
Kilit taşıma katmanının içine takılmıyor. Uygulama bir TLS kitaplığı çağırıyor, kitaplık da TCP’yi çağırıyor; sıra tam olarak bu ve tersi değil. TCP’nin kendisi hiç değişmiyor: aynı sıra numaraları, aynı pencere, aynı bayraklar. Değişen tek şey, sokete düz yazdığın metnin ağa şifreli çıkması.
Bunun pratik karşılığı 443 numaralı port. Kilitli web trafiği oradan geçiyor ama o port sıradan bir port; yol üstündeki yönlendiriciler orada özel hiçbir iş yapmıyor. Şifrelemeyi iki uç kendi arasında hallediyor. Kitaplık web’e özel de değil: isim sorgusunu gizleyen DoH ve DoT da altında aynı kilidi taşıyor.
Kilidin verdiği söz üç parçalı. Gizlilik simetrik şifrelemeden geliyor. Bütünlük kriptografik özetlemeden, kimlik doğrulama ise açık anahtar kriptografisinden. Üçü ayrı ayrı gerekiyor, çünkü şifreleme tek başına kurcalamayı engellemiyor. Okuyamayan biri de bitleri karıştırabilir; kilitli bir kutuyu açamadan sallamaya benziyor.
Kilitsiz HTTP bu üç sözden hiçbirini vermiyor. Hat dinlemeye de ortadaki adam saldırısına da açık. Amaç, dürüst olması beklenmeyen bir ağın üstünde dürüst bir kanal kurmak. Adı hâlâ SSL diye anılan eski protokol 2015’te kullanımdan kaldırıldı; bugünkü sürüm TLS 1.3 ve künyesi RFC 8446.
Tek gidiş-dönüş nasıl mümkün oldu?
El sıkışmanın dört işi var ve dördü de birkaç mesajın içine sığıyor: sürüm pazarlığı yapmak, kriptografik algoritmaları seçmek, karşı tarafı doğrulamak ve ortak anahtar malzemesi kurmak. Bunlar üç evreye dağılmış: Key Exchange, Server Parameters, Authentication. İlk evre bittikten sonra geriye kalan her şeyi şifreli akarken göreceksin; sunucunun sertifikası bile açıkta gitmiyor.
İlk mesaj ClientHello. İçinde 32 bayt uzunluğunda bir rastgele değer, önerilen protokol sürümleri, simetrik şifre ile anahtar türetme işlevinin (HKDF) özet çiftlerinden oluşan liste ve key_share uzantısında bir dizi Diffie-Hellman anahtar payı duruyor. Sürüm alanına ayrıca bak, çünkü tuhaf görünüyor. Adı legacy_version ve içine 0x0303, yani TLS 1.2 yazılıyor; gerçek sürüm ise supported_versions uzantısında 0x0304 olarak duruyor. Sebebi uyumluluk: yol üstündeki eski kutular tanımadıkları bir sürüm numarası görünce bağlantıyı düşürüyordu.
Bir gidiş-dönüşü kurtaran hileyi de burada göreceksin. İstemci sunucunun hangi grubu seçeceğini tahmin ediyor ve anahtar payını daha ilk mesajda gönderiyor. Sunucu kabul ederse ortak sır ikinci mesajda zaten hesaplanabilir hâle geliyor. Tahmin tutmazsa sunucu HelloRetryRequest yolluyor, istemci el sıkışmayı doğru grupla baştan kuruyor ve bir gidiş-dönüş daha ödüyor. El sıkışma özeti sıfırlanmıyor: ilk alışveriş de özete giriyor.
istemci -> sunucu ClientHello random 32 bayt, key_share, supported_versions
sunucu -> istemci ServerHello seçilen takım, sunucunun key_share'i
EncryptedExtensions <-- buradan sonrası şifreli
Certificate
CertificateVerify
Finished
istemci -> sunucu Finished
[uygulama verisi]El sıkışmanın hangi mesajında neyin kararlaştığını sırayla görelim:
ClientHello yalnızca "merhaba" demiyor: desteklediği şifre takımlarını, gerçek sürüm numarasını ve key_share uzantısında bir Diffie-Hellman anahtar payını da taşıyor. Bu pay bir tahmin — sunucunun hangi grubu seçeceği henüz bilinmiyor.- İstemci selamla birlikte bir tahmin yolluyor.
ClientHelloyalnızca "merhaba" demiyor: desteklediği şifre takımlarını, gerçek sürüm numarasını vekey_shareuzantısında bir Diffie-Hellman anahtar payını da taşıyor. Bu pay bir tahmin — sunucunun hangi grubu seçeceği henüz bilinmiyor. - Tahmin tuttuysa ortak sır ikinci mesajda hazır.
ServerHelloseçilen takımı ve sunucunun kendi payını getiriyor. İki pay birleşince ortak sır her iki tarafta hesaplanabilir hâle geliyor. Tahmin tutmazsa sunucu geri çeviriyor ve bir gidiş-dönüş daha ödeniyor. - Buradan sonrası şifreli. Sunucunun geri kalan mesajları el sıkışma trafik anahtarlarıyla korunuyor. Sertifika bile açıkta gitmiyor — yol üstünde dinleyen biri hangi siteye bağlandığını sertifikadan okuyamıyor.
- İki taraf da el sıkışmayı mühürlüyor.
Finishedmesajı o ana kadar geçen bütün mesajların özetini taşıyor. Yolda tek bir alan değiştirilmiş olsa özet tutmaz ve bağlantı kesilir. Pazarlığı geriye çekmeye çalışan bir saldırgan tam burada yakalanıyor. - Fatura: bir gidiş-dönüş. Uygulama verisi üçüncü mesajdan sonra akabiliyor. Bunun altında bir de taşıma el sıkışmasının turu var; ilk bayt gitmeden önce toplam iki gidiş-dönüş ödeniyor. Aynı iş TLS 1.2 üstünde üç tur alıyordu.
Dökümde ServerHello satırına bak: seçilen şifre takımını ve sunucunun kendi payını taşıyor. Hemen ardından gelen EncryptedExtensions, sunucu el sıkışma trafik sırrından türeyen anahtarlarla korunan ilk mesaj. Finished ise doğrulama bloğunun sonuncusu: el sıkışmanın bütünlüğünü ve hesaplanan anahtarların doğruluğunu mühürlüyor. İçeriği yanlış çıkarsa bağlantı decrypt_error uyarısıyla kesiliyor; el sıkışmaya karışan bir kutu varsa tam burada yakalanıyor.
Sertifikayı burada yalnızca sunucunun gönderdiği imzalı belge olarak alıp geçiyorum. Onu kimin imzaladığını ve o imzaya neden güvenildiğini, sertifika zincirini kuran 11. bölümde açacağım.
SYN ve SYN-ACK ile ödediğin bir gidiş-dönüş vardı; kripto el sıkışması onun üstüne bir tane daha koyuyor. Yani ilk uygulama baytın yola çıkmadan önce iki gidiş-dönüş ödemiş oluyorsun. Naif bir tasarımda ya da TLS 1.2 üstünde bu üç gidiş-dönüştü.
Beş takım, iki karar
Şifre takımına baktığında artık yalnızca iki şey göreceksin: kayıtları koruyan AEAD algoritması ve HKDF ile kullanılacak özet algoritması. Anahtar değişimi ile imza algoritması takımın dışına, ayrı uzantılara çıkarıldı. Adlandırma kalıbı bu yüzden sade: TLS_AEAD_HASH.
| Takım | Durum |
|---|---|
| TLS_AES_128_GCM_SHA256 | zorunlu |
| TLS_AES_256_GCM_SHA384 | önerilen |
| TLS_CHACHA20_POLY1305_SHA256 | önerilen |
| TLS_AES_128_CCM_SHA256 | tanımlı |
| TLS_AES_128_CCM_8_SHA256 | tanımlı |
Takım adındaki sayı anahtar uzunluğunu veriyor: ilk satırda 128 bit, ikincisinde 256 bit. Seçenek sayısı bir önceki sürümdeki 37’den 5’e indi ve bu sadeleşmeyi kasıtlı bir güvenlik kararı olarak oku; az seçenek, az yanlış yapılandırma anlamına geliyor.
İkinci karar daha sert çıkıyor: RSA ile anahtar taşıma seçeneği tamamen kaldırıldı ve anahtar değişimi için Diffie-Hellman zorunlu hâle geldi. Bunun sonucuna ileri gizlilik deniyor. Sunucunun uzun ömürlü özel anahtarı sonradan ele geçse bile eski oturumlar açılamıyor, çünkü o oturumların anahtarı hiç telden geçmedi. Matematiğini az önce kurduğun Diffie-Hellman anahtar uzlaşması, protokolde tam olarak burada işe koşuluyor.
Veri korumasında da şifreleme ile doğrulama tek algoritmada birleşti. Eskiden bunları arka arkaya iki ayrı adımda yapıyordun ve sıralamayı yanlış kurmak koca bir saldırı sınıfı üretiyordu.
Anahtarların kendisini kimse elle seçmiyor. Anahtar takvimini HKDF-Extract ve HKDF-Expand üstüne kurmuşlar. Türetme etiketleri tls13 önekiyle başlıyor. Her türetmeye o ana kadarki bütün el sıkışma mesajlarının özeti giriyor. Yani anahtar, konuşmanın kendisine bağlı kalıyor. Ortak sırrı da doğrudan kullanmıyorlar. Ondan yön başına ve evre başına ayrı anahtarlar çıkıyor. Simetrik şifrelemenin anahtarını işlere bölme kuralı vardı; HKDF takvimi tam olarak onu uyguluyor.
DerinleşmeKuantum sonrası anahtar değişimi el sıkışmaya kaç bayt ekliyor?
Melez anahtar uzlaşması RFC 10024 ile standartlaştı. Tanımı sade: birden çok anahtar değişim algoritması aynı anda çalışıyor ve çıktıları birleştiriliyor, bileşenlerden en az biri sağlam kaldığı sürece sonuç güvenli sayılıyor. Bugünkü melezler ML-KEM ile geçici eliptik eğri Diffie-Hellman’ı birleştiriyor.
Üç grup tanımlı. X25519MLKEM768 kayıt değeri 4588 ile önerilen olan, SecP256r1MLKEM768 4587, SecP384r1MLKEM1024 4589. Standart öncesi deneysel kod noktaları aynı belgeyle geçersiz ilan edildi.
Fatura şurada. X25519MLKEM768 seçildiğinde istemcinin anahtar payı 1.216 bayt: 1.184 bayt ML-KEM kapsülleme anahtarı ve 32 bayt X25519. Sunucunun payı 1.120 bayt ve içinde 1.088 baytlık ML-KEM şifreli metni var. Ortaya çıkan ortak sır 64 bayt. NIST eğrisiyle kurulan SecP256r1MLKEM768 grubunda istemci payı 1.249 bayt.
Düz X25519 payı ise yalnızca 32 bayt. Aradaki fark istemci yönünde 1.184 bayt, sunucu yönünde 1.088 bayt; el sıkışma başına 2.272 bayt ek yük.
Bu yükün neden hissedildiğini görmek için tek bir hesap yetiyor. Bir çerçevenin taşıyabileceği Ethernet yükü 1.500 bayt. Ondan 20 bayt IPv4 ve 20 bayt TCP başlığı düşünce geriye 1.460 bayt kalıyor. 1.216 baytlık anahtar payı bunun %83’ünü dolduruyor ve ClientHello mesajının geri kalanına 244 bayt yer bırakıyor.
Boyut büyüyünce doğrulama da zorunlu hâle geliyor: sunucu istemcinin kapsülleme anahtarını denetlemek, geçersizse bağlantıyı illegal_parameter uyarısıyla kesmek zorunda. İstemci de şifreli metnin uzunluğunun seçilen gruba uyduğunu doğruluyor.
Kayıtlara bölmek, kapanışı ilan etmek
El sıkışma bitince iş kayıt katmanına kalıyor. Kayıt katmanı dört içerik türü tanıyor: handshake, application_data, alert ve yalnızca eski yazılımlarla uyum için duran change_cipher_spec. Böylece birden çok üst protokol aynı kayıt katmanını paylaşabiliyor.
Veri en fazla 16.384 baytlık parçalara bölünüyor ve her parça kendi bütünlük etiketini taşıyor. Bölmenin sebebi şu: alıcı bağlantının kapanmasını beklemeden her kaydı geldiği anda doğrulayıp kullanabiliyor.
Kapanış da protokolün içinde. Saldırgan sahte bir TCP kapatma parçası üretirse taraflardan biri veriyi olduğundan kısa sanır; buna kesme saldırısı deniyor. Çözüm kapanışa ayrı bir kayıt türü vermek; o tür de bütünlük hesabına giriyor. Her taraf yazma yönünü kapatmadan önce close_notify göndermek zorunda, ve o uyarıdan sonra gelen veri yok sayılıyor. Taşımanın FIN’i tek başına yeterli kanıt sayılmıyor.
İkinci ziyaret daha ucuz. İlk el sıkışma bittikten sonra sunucu NewSessionTicket ile istemciye bir PSK kimliği veriyor; istemci sonraki el sıkışmada pre_shared_key uzantısıyla o kimliği sunuyor. Sunucu kabul ederse yeni bağlantı kriptografik olarak eskisine bağlanıyor ve tam el sıkışma yerine eski anahtardan başlanıyor. Sunucu PSK ile doğrulandığı için Certificate ve CertificateVerify mesajlarını hiç göndermiyor.
Eski sürümlerdeki oturum kimliği ve oturum bileti mekanizmalarının ikisi de kaldırıldı; yerlerini önceden paylaşılan anahtar aldı. Ama PSK tek başına kullanılırsa uygulama verisi için ileri gizlilik kayboluyor. Bu yüzden istemci PSK sunarken key_share da gönderiyor: hem tazelik gelsin, hem de sunucu sürdürmeyi reddedip tam el sıkışmaya düşebilsin diye.
Paylaşılan anahtar varsa istemci daha ileri gidebiliyor. İlk mesaj demetinde early_data uzantısıyla birlikte şifrelenmiş bir HTTP isteği gidiyor ve el sıkışma gecikmesi sıfıra iniyor. Bedeli de var. Erken veri yalnızca sunulan PSK’dan türeyen anahtarlarla korunuyor, yani ileri gizliliği yok. Kaydedilip sonradan aynen tekrar oynatılabiliyor da.
DerinleşmeSıfır gidiş-dönüşlü veri neden tekrar oynatma saldırısına açık?
Bu zayıflık standart metninde açıkça sayılıyor. Tekrar oynatma korumasının nereden geldiğine bakınca sebebi de görünüyor. Sıradan veri, sunucunun ServerHello içinde ürettiği rastgele değerden türeyen anahtarlarla korunuyor; kaydedilmiş bir kayıt ikinci bir oturumda anlamını yitiriyor. Erken veri ise sunucu daha tek kelime etmeden yazılıyor. O demeti kaydeden biri, aylar sonra aynı baytları aynı sunucuya yeniden sunabiliyor ve sunucu bunu ilk gelişinden ayıramıyor.
Standart bir savunma dayatıyor ama sınırını da söylüyor: sunucunun her örneği aynı erken el sıkışmayı en fazla bir kez kabul etmek zorunda. Bu, tekrar oynatma sayısını sunucu örneği sayısıyla sınırlıyor, sıfıra indirmiyor. Yöntemlerden ikisi tek kullanımlık bilet vermek ve yakın zamanda görülen ClientHello mesajlarını kaydedip yinelenenleri reddetmek.
İstemci sunucunun hangisini uyguladığını bilemiyor. Tarayıcının sınırı buradan geliyor: erken veriye yalnızca sunucu durumunu değiştirmeyen okuma istekleri konuyor.
Çok bölgeli bir kurulumda ayrı bir tuzak var. Saldırgan aynı ClientHello ile erken veriyi iki bölgeye birden gönderirse birinde sıfır gidiş-dönüşlü kip kabul edilir, diğerinde tam el sıkışmaya zorlanır. İstemci isteği yeniden denediğinde iş sistem genelinde iki kez yapılmış olur.
Bütün bunlar kendi makinende görünür hâle gelebiliyor. openssl s_client -connect ornek-site.test:443 -tls1_3 çıktısında Protocol, Cipher ve Server Temp Key satırlarına bak; sırasıyla pazarlıkla seçilen sürümü, şifre takımını ve anahtar uzlaşma grubunu gösteriyorlar. Kendi sunucunu belgeleme için ayrılmış 203.0.113.10 adresinde dinletiyorsan hedefi doğrudan o sayıyla da verebilirsin.
Özet
Peki, ne öğrendik?
- Kilit uygulama katmanında kuruluyor: TCP hiç değişmiyor, sokete düz yazılan metin ağa şifreli çıkıyor ve varsayılan port 443.
- El sıkışma tek gidiş-dönüşe bir tahminle sığıyor: istemci grubu önceden seçip anahtar payını ilk mesajda gönderiyor, tutmazsa
HelloRetryRequestbir tur daha yazıyor.- Şifre takımı artık yalnızca AEAD ile özet algoritmasını seçiyor: anahtar değişimi ile imza dışarı çıktı, seçenek 37’den 5’e indi ve Diffie-Hellman zorunlu oldu.
- Kayıt katmanı akışı parçalara bölüyor: her kayıt kendi başına doğrulanıyor ve kapanış
close_notifyile ilan ediliyor.
Kanal artık okunamıyor, kurcalanamıyor ve kapanışı bile sahtelenemiyor. Kilit kuruldu; ama kilidi karşı tarafla kurduğunu nereden biliyorsun?
11. bölüme devam et: Sertifika Zinciri