Ana içeriğe geç

Bölüm 11: TLS El Sıkışması

9 dakikalık okumaGüncelleme:

Ağlar'a “Sen”i Katmak kitabının bir bölümü: adres çubuğuna yazdığın tek bir isteğin peşinden bakırla fiberin fiziğine inip karşı taraftan geri dönen uzun bir teknik yolculuk.

Kısım 2: Bağlantı Kuruluyor (Bölüm 6-12)

Tüm bölümler
  1. Giriş
  2. Adres Çubuğundan İsteğe
  3. Paket Anahtarlamalı Yol
  4. İsimden Adrese
  5. DNS Kayıtları
  6. Önbellek, Tazelik ve Güven
  7. Port Numarası ve Soket
  8. Bağlantısız Taşıma: UDP
  9. Üç Adımlı El Sıkışma
  10. Simetrik Şifreleme
  11. Açık Anahtarlı Kriptografi
  12. TLS El Sıkışması
  13. Sertifika Zinciri
  14. HTTP Mesajının Anatomisi
  15. Durum, Çerez ve Oturum
  16. HTTP/2'den HTTP/3'e
  17. İçerik Dağıtım Ağları
  18. Diğer Uygulama Protokolleri
  19. Katmanlı Mimari
  20. Hizmet ve Protokol
  21. Encapsulation ve Başlıklar
  22. Paket Yakalama ve Çözümleme
  23. Ağ Interface Kartı
  24. Fiziksel Adresleme
  25. Hata Sezme ve CRC
  26. Adres Çözümleme: ARP
  27. Ortam Erişimi: CSMA/CD
  28. Switch'ler
  29. Ağ Topolojileri
  30. Sanal Yerel Ağlar
  31. Kablosuz Erişim: 802.11
  32. Radyo Kanalı ve Kapasite
  33. Kablosuz Ağ Güvenliği
  34. Hücresel Ağlar
  35. Spektrum ve Kuşaklar
  36. Hareketlilik ve Aktarma
  37. Adres Kiralama: DHCP
  38. Adresin Anatomisi
  39. Adres Tükenmesi ve IPv6
  40. Router Mimarisi
  41. Kuyruk Yönetimi
  42. Cihaz Yapılandırma
  43. Routing Protokolleri
  44. Kontrol Düzlemi ve SDN
  45. Ağ İzleme ve Yönetimi
  46. Alanlar Arası Routing
  47. Adres Çevirisi
  48. Ara Kutular
  49. Tünelleme ve IPsec
  50. Firewall'lar
  51. Ağ Saldırıları
  52. Operatör ve Metro Ağları
  53. Bitten Sinyale
  54. Sembol Hızı ve Kanal Kapasitesi
  55. Multiplexing Teknikleri
  56. İletim Ortamları
  57. Bakır Kablo Sonlandırma
  58. Fiber Sonlandırma
  59. Fiber Optik İletim
  60. Bakır Erişim Ağı: DSL
  61. DSL'de Hız ve Mesafe
  62. Pasif Optik Ağlar
  63. Kablo Erişim Ağları
  64. Kablo Modem ve CMTS
  65. Denizaltı Kabloları
  66. Veri Merkezi Ağları
  67. Encapsulation Çözme
  68. Güvenilir Aktarım
  69. Tıkanıklık Denetimi
  70. Trafik Çözümleme
  71. Gecikme Bütçesi
  72. Soket Programlama
  73. Eşten Eşe ve Protokol Tasarımı
  74. Uyarlanır Video Akışı
  75. Gerçek Zamanlı Ses ve Görüntü
  76. Kablosuz Sensör Ağlarının Mimarisi
  77. Sensör Ağlarında Ortam Erişimi
  78. Sensör Ağlarında Yönlendirme ve IP
  79. Zaman Eşitleme ve Konum Bulma
  80. Sensör Ağı Uygulamaları
  81. Mobil Kablosuz Sensör Ağları
  82. 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 neyi pazarlıyor?

Kilit nereye takılıyor?

Kilidin adı TLS (Transport Layer Security) ve adındaki “transport layer” yanıltıcı: kilit transport layer’ın içine takılmıyor, onun bir üstünde, programların kendi dilini konuştuğu application layer’da (uygulama katmanı) kuruluyor. Uygulama bir TLS kitaplığı çağırıyor, kitaplık da TCP’yi çağırıyor; sıra 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 router’lar 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 MITM 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.

ClientHello’nun bir uzantısı daha var ve ilk durakta bıraktığım düğümü o çözüyor: SNI (Server Name Indication, server_name uzantısı). Aynı adres ve portta yüzlerce siteyi barındıran bir sunucu hangi sertifikayı sunacağına ancak istenen adı görerek karar verebilir; ama sertifika gelmeden şifreleme de kurulamaz. Çözüm, adı ilk mesajda, henüz şifresizken göndermek. Bedeli şu: ClientHello açıkta gittiği için yolun üstündeki herkes hangi siteye bağlandığını görüyor. Şifreli ad çözmenin kapatamadığı sızıntı buydu. Onu da kapatan uzantının adı ECH (Encrypted Client Hello): sunucunun açık anahtarı DNS’teki HTTPS kaydından alınıyor ve ad o anahtarla şifrelenip gönderiliyor. Yol üstündeki kutu artık yalnızca barındırma sağlayıcısının herkese açık ön adını görüyor, asıl adı değil.

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:

Bir gidiş-dönüşte kilit
istemcisunucuClientHello · key_share · supported_versionsServerHello · key_shareşifreliEncryptedExtensions · CertificateCertificateVerify · FinishedFinishedilk uygulama verisitoplam iki gidiş-dönüş: biri taşımanın el sıkışması, biri bu — TLS 1.2'de üç tuttu
İstemci selamla birlikte bir tahmin yolluyor. 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.
  1. İstemci selamla birlikte bir tahmin yolluyor. 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.
  2. Tahmin tuttuysa ortak sır ikinci mesajda hazır. ServerHello seç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.
  3. 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 sertifikayı okuyamıyor. Sitenin adı ise ECH kullanılmıyorsa ClientHello içindeki SNI alanından hâlâ görünüyor.
  4. İki taraf da el sıkışmayı mühürlüyor. Finished mesajı 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.
  5. 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 12. 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. 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ımDurum
TLS_AES_128_GCM_SHA256zorunlu
TLS_AES_256_GCM_SHA384önerilen
TLS_CHACHA20_POLY1305_SHA256önerilen
TLS_AES_128_CCM_SHA256tanımlı
TLS_AES_128_CCM_8_SHA256tanımlı

Takım adındaki sayı anahtar uzunluğunu veriyor: ilk satırda 128 bit, ikincisinde 256 bit. Seçenek sayısı, TLS 1.2 standardının kendi metninde tanımladığı 37 takımdan 5’e indi; sonradan eklenenlerle o sürümün kayıt defteri yüzlerce takımı buluyordu. 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 (forward secrecy) 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 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; POODLE ve Lucky Thirteen o sınıfın en bilinen örnekleri.

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 o kuralı 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 encapsulation 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 frame’in 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 encapsulation 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. 10. bölümdeki openssl s_client komutunu grep olmadan çalıştır ve tam çıktıda üç satıra bak: Protocol, Cipher ve Negotiated TLS1.3 group (eski OpenSSL sürümlerinde sonuncusunun adı Server Temp Key). Sırasıyla pazarlıkla seçilen sürümü, şifre takımını ve anahtar uzlaşma grubunu gösteriyorlar.

Özet

  • Kilit application layer’ı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 HelloRetryRequest bir 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ı stream’i parçalara bölüyor: her kayıt kendi başına doğrulanıyor ve kapanış close_notify ile ilan ediliyor.

Kanal artık okunamıyor, kurcalanamıyor ve kapanışı bile sahtelenemiyor. Kilit kuruldu; ama kilidi karşı tarafla kurduğunu nereden biliyorsun?

12. bölüme devam et: Sertifika Zinciri