Ana içeriğe geç

Bölüm 13:Durum, Çerez ve Oturum

10 dakikalık okumaGüncelleme:

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
  1. Giriş
  2. Adres Çubuğundan İsteğe
  3. Paket Anahtarlamalı Yol
  4. İsimden Adrese
  5. Önbellek, Tazelik ve Güven
  6. Port Numarası ve Soket
  7. Bağlantısız Taşıma: UDP
  8. Üç Adımlı El Sıkışma
  9. Simetrik Şifreleme
  10. Açık Anahtarlı Kriptografi
  11. TLS El Sıkışması
  12. Sertifika Zinciri
  13. HTTP Mesajının Anatomisi
  14. Durum, Çerez ve Oturum
  15. HTTP/2'den HTTP/3'e
  16. İçerik Dağıtım Ağları
  17. Diğer Uygulama Protokolleri
  18. Katmanlı Mimari
  19. Kapsülleme ve Başlıklar
  20. Paket Yakalama ve Çözümleme
  21. Ağ Arayüz Kartı
  22. Fiziksel Adresleme
  23. Hata Sezme ve CRC
  24. Adres Çözümleme: ARP
  25. Anahtarlama ve Ortam Erişimi
  26. Ağ Topolojileri
  27. Sanal Yerel Ağlar
  28. Kablosuz Erişim: 802.11
  29. Radyo Kanalı ve Kapasite
  30. Kablosuz Ağ Güvenliği
  31. Hücresel Ağlar
  32. Spektrum ve Kuşaklar
  33. Hareketlilik ve Aktarma
  34. Adres Kiralama: DHCP
  35. Adresin Anatomisi
  36. Adres Tükenmesi ve IPv6
  37. Yönlendirici Mimarisi
  38. Kuyruk Yönetimi
  39. Cihaz Yapılandırma
  40. Yönlendirme Protokolleri
  41. Kontrol Düzlemi ve SDN
  42. Ağ İzleme ve Yönetimi
  43. Alanlar Arası Yönlendirme
  44. Adres Çevirisi
  45. Ara Kutular
  46. Tünelleme ve IPsec
  47. Güvenlik Duvarları
  48. Operatör ve Metro Ağları
  49. Bitten Sinyale
  50. Çoklama Teknikleri
  51. İletim Ortamları
  52. Kablo Sonlandırma
  53. Fiber Optik İletim
  54. Bakır Erişim Ağı: DSL
  55. Pasif Optik Ağlar
  56. Kablo Erişim Ağları
  57. Denizaltı Kabloları
  58. Veri Merkezi Ağları
  59. Kapsülleme Çözme
  60. Güvenilir Aktarım
  61. Tıkanıklık Denetimi
  62. Trafik Çözümleme
  63. Gecikme Bütçesi
  64. Son Söz

Bir önceki bölümde yazdığın istekte seni tanıtan tek bir alan yoktu. Sunucu da geçmişi tutmuyor.

Yine de siteler seni hatırlıyor. Bunu yapan şey sunucunun belleği değil, geçen sefer verdiği kâğıdı geri götürmen. O kâğıt neye benziyor?

Sunucu neden hiçbir şey tutmuyor?

Her HTTP isteği ötekilerden bağımsız okunuyor. Bunun protokoldeki adı durumsuzluk. Çok adımlı işlem diye bir kavram yok, ve adımları birbirine bağlama işi protokolün dışında kalıyor.

Durumu bir yerde tutmanın iki yolu var ve ikisi de bu kitapta karşına çıkıyor. Birincisi durumu protokolün uçlarında tutmak. Bağlantı kuran taşımada iki tarafın da karşısındakinin nerede kaldığını gösteren bir kaydı var. İkinci yol durumu mesajların içinde taşımak.

Çerezin yaptığı iş tam olarak bu. Durumu sunucunun belleğinde tutmak yerine senin makinene yazıyor ve her istekte onu geri getirtiyor. Fikir yeni değil: sunucu yarı açık bağlantı durumunu ikinci mesajın sıra numarasına kodlayıp kuyruğu tümden ortadan kaldırabiliyordu. Çerez aynı işi bir kat yukarıda, uygulama katmanında yapıyor.

Çerez dört parçadan kurulur

Mekanizma dört parçayı birden istiyor. İlk ikisi mesajların içinde: yanıt mesajındaki Set-Cookie başlık satırı ve sonraki istek mesajındaki Cookie başlık satırı. Diğer ikisi mesajların dışında: tarayıcının makinende yönettiği çerez dosyası ve sitenin arka ucundaki veritabanı. Dördü bir arada olmadan zincir tamamlanmıyor.

Zincir ilk istekte başlıyor. Siteye ilk vardığında site bir kimlik üretiyor ve veritabanında ona bir kayıt açıyor. Kimliği de yanıtın başlığına yazıyor.

# sunucudan gelen yanıtta
Set-Cookie: oturum=7c1a94e0b3f582d6a4e71b09; Path=/; Max-Age=1800; Secure; HttpOnly

# sonraki her istekte tarayıcıdan giden
Cookie: oturum=7c1a94e0b3f582d6a4e71b09

Sonraki isteklerin hepsi o değeri taşıyor. Sunucunun seni tanıması tam olarak şu: gelen satırdaki değeri okumak ve veritabanındaki kaydı bulmak. Tanıdığı şey sen değilsin, taşıdığın numara. Bütün bunu tanımlayan belge RFC 6265, Nisan 2011 tarihli ve tam adı HTTP State Management Mechanism.

Değerin ne anlama geldiğine protokol değil uygulama karar veriyor. Protokol yalnızca taşımayı tarif ediyor, ve gelen Cookie satırını yok saymak sunucunun hakkı.

Tek bir yanıt birden çok Set-Cookie satırı taşıyabiliyor. Bu satırlar tek satıra katlanmıyor: virgül karakteri Set-Cookie’nin kendi sözdiziminde kullanılıyor ve katlama anlamı bozuyor.

Ad ve değerden sonrası kapsamı çiziyor

ÖznitelikNe yapıyor
Max-Ageçerezin en fazla kaç saniye yaşayacağını söylüyor
Expiresaynı şeyi bir tarih olarak söylüyor; ikisi birden varsa Max-Age üstün geliyor
Domainçerezin hangi makinelere gönderileceğini belirliyor, alt adlar da kapsama giriyor
Pathçerezi belli bir dizin ve altına sınırlıyor, ama güvenlik için ona güvenilmiyor
Secureçerezi yalnızca güvenli kanaldan gönderilir kılıyor
HttpOnlyçerezi HTTP isteklerine ayırıyor, sayfadaki betikler onu okuyamıyor
SameSiteçerezin başka bir siteden tetiklenen isteklere eklenip eklenmeyeceğini belirliyor

Örnekteki çerez 1.800 saniye, yani 30 dakika yaşıyor. Süre hiç yazılmasaydı çerez oturum bitene kadar saklanacaktı, yani tarayıcı kapanınca düşecekti. Oturum çerezi ile kalıcı çerez arasındaki fark tam olarak bu.

Secure yazan çerez yalnızca 443 numaralı portun açtığı kanaldan gidiyor. Koruduğu şey gizlilik, bütünlük değil: etkin bir saldırgan güvensiz kanaldan aynı adı taşıyan bir çerez göndererek onun üstüne yazabiliyor. HttpOnly ile Secure birbirinden bağımsız, bir çerez ikisini birden taşıyabilir.

Standart, genel amaçlı bir tarayıcıdan çerez başına en az 4.096 bayt saklamasını istiyor. Ad, değer ve öznitelikler bu toplama giriyor. Alan adı başına en az 50, toplamda en az 3000 çerez saklanması bekleniyor.

Sunucuların az ve küçük çerez kullanması bekleniyor, çünkü Cookie satırı her isteğe ekleniyor ve upstream (yukarı yön) bant genişliğinden yiyor. Örnekteki adın uzunluğu 6 bayt, kimlik 24 onaltılık basamak, ad-değer çifti 31 bayt ediyor. Başlık adı ve satır sonuyla birlikte Cookie satırı 41 bayt tutuyor. Bir sayfa aynı kaynaktan 60 nesne çekiyorsa aynı çerez tek sayfa yüklemesinde upstream tarafında 2.460 bayt yük üretiyor.

Tarayıcının çerezi kendiliğinden eklemesi bir açık üretiyor

Tabloya yeni eklenen son satır bir savunma ve neyi savunduğunu bilmeden anlamı yok.

Tarayıcının kuralı sade: bir adrese istek giderken, o adrese ait çerezleri kim istemiş olursa olsun ekliyor. Bankana giriş yaptın, oturum çerezin duruyor. Sonra başka bir sekmede bambaşka bir siteyi açıyorsun ve o sayfanın içinde görünmez bir form ya da bir görsel etiketi, senin bankana bir istek yaptırıyor. Tarayıcı o isteğe oturum çerezini de ekliyor — çünkü kuralı bu. Banka isteği senden gelmiş sayıyor.

Saldırının adı siteler arası istek sahteciliği (CSRF). Saldırganın oturum çerezini okuması gerekmiyor, hatta görmesi bile gerekmiyor; yalnızca kullandırması yetiyor. HttpOnly bu saldırıya karşı hiçbir şey yapmıyor, çünkü çerez okunmuyor.

SameSite özniteliği tam buraya oturuyor ve üç değer alıyor:

değerbaşka siteden gelen isteğe çerez eklenir mi
Stricthiçbir durumda eklenmez
Laxyalnızca üst düzey gezinmede ve güvenli metotlarda eklenir
Noneher zaman eklenir; Secure ile birlikte yazılması zorunlu

Lax bugünün varsayılanı ve bir ödünleşim: başka bir siteden gelen bir bağlantıya tıkladığında oturumun açık kalıyor, ama aynı siteden tetiklenen bir form gönderimine çerez eklenmiyor. None ise üçüncü taraf çerezleri için kalan tek yol.

İkinci bir savunma da adın kendisinde. Bir çerezin adı __Host- ile başlıyorsa tarayıcı onu yalnızca Secure işaretliyse, Domain yazılmamışsa ve Path kökse kabul ediyor. Böylece o çerezin tam olarak hangi makineye ait olduğu adın içinde kilitleniyor ve bir alt alan adı onu üstüne yazamıyor. __Secure- öneki daha gevşek, yalnızca Secure şartını koyuyor.

Numaranın zarafeti şurada: kural sunucuda değil, adın kendisinde taşınıyor. Çerezi kim gönderirse göndersin, tarayıcı adı okuyup şartı uyguluyor.

Birinci taraf çerez, ziyaret etmeyi seçtiğin siteden geliyor. Üçüncü taraf çerez hiç seçmediğin bir siteden geliyor, sayfanın içine gömülü bir nesne onu çekiyor. Kalıcı üçüncü taraf çerezleri aynı kimlik değerini birden çok sitede görünür kılıyor, ve izleme tam olarak o ortak değerin farklı sitelerde tekrar tekrar geri gönderilmesi. Görünür bir reklam bile gerekmiyor. Görünmez bir bağlantı aynı işi görüyor.

İki farklı sitenin sayfası yan yana; ikisinin de üstünde aynı üçüncü siteden gelen bir reklam şeridi duruyor ve oklar iki sayfadan da o tek kaynağa iniyor. Gömülü nesnenin aynı çerezi iki sitede birden görünür kılması.

Bir not: Türkçe kaynaklarda “HTTP oturumu” çoğu zaman tek bir istek-yanıt turu anlamına geliyor. Bu kitapta oturum, protokolün üstünde çerezle kurulan kullanıcı oturumudur; 10. bölümde el sıkışmanın sonunda üretilen oturum anahtarıyla akrabalığı yok.

DerinleşmeHTTP durum tutma yükünü neden hiç üstlenmedi?

Durum tutan protokoller karmaşıklaşıyor. İki uçtan biri çökerse iki taraftaki durum görüntüleri birbirini tutmuyor ve uzlaştırılması gerekiyor. Durumlu bir protokolde bir işlemin iki adımı ya birlikte oluyor ya hiç olmuyor, yani yarım kalmış işlemden kurtarma yükü doğuyor. HTTP bu yükü hiç üstlenmemeyi seçti.

Bedeli mesajın içinde taşınan değerin görünürlüğü. İzlemede mekanizmanın kendisi hiç değişmiyor: aynı satır, aynı başlık, aynı veritabanı sorgusu. Değişen tek şey çerezi kimin koyduğu.

Bu yüzden savunma protokolde değil, protokolün dışında kuruldu. Üçüncü taraf izleme çerezleri Firefox ve Safari’de varsayılan olarak kapatıldı, Chrome için de kapatma kararı açıklandı. Hukuk da aynı yerden giriyor: GDPR’a göre bir çerez kişiyi teşhis edebiliyorsa kişisel veri sayılıyor ve kişisel veri kurallarına tabi oluyor. Kullanıcının çerezlere izin verip vermeme konusunda açık denetimi olması gerekiyor, ve sitelerde karşına çıkan onay kutularının dayanağı bu.

Aynı nesneyi kaç kez indirmen gerekiyor?

Bir web önbelleği istekleri asıl sunucuyu hiç işin içine katmadan karşılıyor. Nesne depoda varsa istemciye oradan dönüyor ve istek dışarı çıkmıyor. Yoksa önbellek nesneyi asıl sunucudan istiyor, gelen kopyayı saklıyor, aynı kopyayı istemciye veriyor. Kaçırılan isabet sonraki isabeti hazırlıyor.

Bu kutu iki rolü aynı anda oynuyor: isteği yapan istemciye karşı sunucu, asıl sunucuya karşı istemci. Adı vekil sunucu.

Kazanç iki tane. Birincisi yanıt süresinin kısalması, çünkü kopya sana daha yakın duruyor. İkincisi kurumun dış erişim hattındaki trafiğin azalması. Asıl dert de o hattın doluluğu: kullanım oranı yükseldikçe kuyruk gecikmesi patlıyor.

Bir kurumun erişim hattı 10 Mbit/s olsun. Kullanıcılar saniyede 40 istek yapıyor ve ortalama nesne 200 kbit. Önbelleksiz durumda hattan 8 Mbit/s geçiyor, yani hat %80 dolu. İsabet oranı %55 olduğunda dışarı yalnızca %45 çıkıyor: 3,6 Mbit/s ve %36 doluluk. Asıl sunucudan gelen istek 1,2 saniye, önbellekten gelen 5 ms sürüyorsa ortalama 0,54 saniyeye iniyor. Hiç kapasite satın alınmadı.

Önbellekler ağın ortasına yerleşmiş ara kutulardan biri. Servis sağlayıcıda, mobil ağda ve içeriğin kopyalarını dünyaya yayan içerik dağıtım ağlarında duruyorlar, ve tek bir sayfa isteği yol boyunca birden çok önbelleğe uğrayabiliyor. İsteğin doğuşunda andığım tarayıcı önbellekleri bu zincirin hem ilk halkası hem de sana en yakın duranı.

RFC 9111 iki tür ayırıyor. Paylaşılan önbellek birden çok kullanıcı için saklıyor ve genellikle ara kutu olarak konuşlanıyor. Özel önbellek tek kullanıcıya ait, çoğunlukla tarayıcının içinde duruyor. Tanım da sade: HTTP önbelleği, yanıt mesajlarının yerel deposu ve depoyu yöneten alt sistem. Tünel rolündeki bir taraf önbellek kullanamıyor.

Yaşı tazelik ömrünü aşmamış yanıt taze, aşmış olan bayat. Taze yanıt asıl sunucuya hiç sorulmadan kullanılıyor. Yanıtın yaşı, asıl sunucuda üretildiği ya da orada başarıyla doğrulandığı andan beri geçen süre, ve Age başlığı bu tahmini saniye olarak taşıyor.

Süreye sunucu karar veriyor, kararını da yanıt başlığında bildiriyor.

YönergeNe diyor
max-ageyaş belirtilen saniyeyi geçince yanıt bayat sayılıyor
no-storeyanıtın hiçbir parçası saklanmıyor
no-cachesaklanabiliyor, ama doğrulanmadan kullanılamıyor
privateyanıt tek kullanıcıya ait, paylaşılan önbellek saklayamıyor

Ortadaki iki satır sık karıştırılıyor. Yasak farklı: no-store saklamayı, no-cache doğrulamadan kullanmayı engelliyor. Tarayıcının kendi önbelleği private yazan yanıtı yine de saklayabiliyor.

Çerez başlığının varlığı yanıtın önbelleklenmesini kendiliğinden engellemiyor. Kişiye özel bir yanıtı paylaşılan önbellekten korumak istiyorsan private ya da no-store’u açıkça yazman gerekiyor.

Sunucu hiç süre vermediyse önbellek kendi tahminini yapıyor. Last-Modified tarihine bakıp o tarihten beri geçen sürenin %10’unu ömür sayması yaygın bir seçim. O ömür dolana kadar oradaki kopya taze sayılıyor, gerçekte eskimiş olsa bile. Her önbellek tazelik ile doğruluk arasında bir takas yapıyor. Aynı takas, yaşam süresi adıyla, önbellek katmanlarının bir kat aşağısında da vardı.

Kopyan hâlâ geçerli mi?

Koşullu istek, metodun uygulanmasından önce sınanacak bir önkoşul taşıyan istek. Önkoşul tutmazsa metot hiç uygulanmıyor. Önbellek tazelemenin en verimli yolu koşullu GET. Aynı mekanizma PUT ile DELETE’te kayıp güncelleme sorununu da önlüyor.

Önkoşulda kullanılabilen kaynak üstverisine doğrulayıcı deniyor. HTTP iki tane tanımlıyor.

Last-Modified gösterimin son değişme anını HTTP tarih biçiminde ve saniye çözünürlüğünde taşıyor. ETag ise opak: içinde ne yazdığı istemciyi ilgilendirmiyor, yalnızca eşit mi değil mi diye karşılaştırılıyor. Tırnak içinde bir dizi. Zayıf üretilmiş etiketin başına W/ öneki konmak zorunda, varsayılanı güçlü doğrulayıcı. Saniye çözünürlüğü yetmediğinde ya da değişiklik tarihi düzgün tutulmadığında tarihten daha güvenilir çalışıyor.

İstemci koşulu iki başlıktan biriyle kuruyor. If-None-Match isteği, elindeki etiketlerden hiçbirinin eşleşmemesi koşuluna bağlıyor; eşleşme varsa koşul yanlış oluyor. If-Modified-Since ise GET ya da HEAD’i gösterimin değişme tarihine bağlıyor, veri değişmediyse hiç aktarılmıyor. İkisi birden gelirse tarih yok sayılıyor, çünkü etiket daha kesin sayılıyor. Birlikte gönderilmelerinin tek sebebi, If-None-Match’i gerçeklemeyen eski ara kutularla uyum.

Koşul yanlış çıkarsa sunucu GET ve HEAD için 304, diğer metotlar için 412 dönmek zorunda. Bu iki kodun sınıfı, durum kodlarını ilk rakama göre ayırırken sabitlenmişti. 304 Not Modified, koşul yanlış çıkmasaydı 200 dönecek olan bir isteğin yanıtı. Sunucu istemciyi kendi kopyasına yönlendiriyor. Yanıt başlık bölümü bitince bitiyor.

Boş dönmüyor ama. 304 üreten sunucu, aynı isteğe 200 dönseydi göndereceği Content-Location, Date, ETag ve Vary alanlarını, ayrıca Cache-Control ile Expires’ı yine göndermek zorunda.

HTTP/1.1 304 Not Modified
Date: Fri, 21 Aug 2026 09:14:02 GMT
ETag: "b41f-63c0a7d1e2"
Cache-Control: max-age=1800

Dört satır ve kapanış boş satırı, toplam 120 bayt. Tam gövdeli karşılığı 18.432 baytlık bir stil dosyası olsaydı fark 153 kat olurdu.

Kazanç turda değil, taşınan baytta. Koşullu istek de tam bir gidiş-dönüş ödüyor: soru çıkıyor, cevap dönüyor. Nesne hiç iletilmediği için aktarım gecikmesi ödenmiyor ve ağ kaynağı harcanmıyor.

Bunu kendi makinende iki adımda görebilirsin. Önce yalnızca başlıkları çekip last-modified satırını kopyalıyorsun, sonra o tarihi koşul olarak geri gönderiyorsun.

curl.exe -sI https://example.com/
curl.exe -sI -H "If-Modified-Since: <yukarıdaki tarih>" https://example.com/

İkinci çıktının ilk satırı HTTP/1.1 304 Not Modified oluyor ve gövde hiç gelmiyor. Aynı çıktıda etag ve Age satırları da duruyor, yani doğrulayıcıyı ve paylaşılan bir önbelleğin yaşını tek komutta görüyorsun. example.com belgeleme için ayrılmış bir ad.

Özet

Peki, ne öğrendik?

  • HTTP hiçbir şey tutmuyor: her istek ötekilerden bağımsız okunuyor ve adımları birbirine bağlayan şey protokolün dışında kalıyor.
  • Çerez durumu senin makinene koyuyor: Set-Cookie yazıyor, Cookie geri getiriyor, tarayıcı saklıyor, veritabanı karşılığını tutuyor.
  • Öznitelikler ömrü ve kapsamı çiziyor: Max-Age süreyi, Domain ile Path yeri, Secure ile HttpOnly erişimi sınırlıyor.
  • Önbellek kapasite satın almadan doluluğu düşürüyor: kopya sana yaklaşıyor ve dış hattın kullanım oranı geriliyor.
  • Koşullu istek baytı kurtarıyor, turu değil: soru yine gidip geliyor, ama nesne hiç iletilmiyor.

Tek bir nesne için bütün bu yolu yürüdün: bir kimlik, bir saklama kararı, bir de tazelik sorusu. Ama açtığın sayfa tek nesne değil; onlarca parçadan kurulu. Bir sayfa yüzlerce parçadan oluşuyorsa, her parça için bu yolu baştan mı yürüyeceksin?

14. bölüme devam et: HTTP/2'den HTTP/3'e