Cookie Canavarlarını Evcilleştirmek: Web Güvenliğinde Çerez Yönetimi

Cookie Security

Cookie Security kısmına geçmeden önce Cookie nedir ve neden ihtiyacımız vardır onu bir irdeleyelim. Bu yazıda, cookie güvenliğinin derinliklerine inecek, temel mekanizmaları anlayacak ve web uygulamalarınızı nasıl daha güvenli hale getirebileceğinizi pratik örneklerle göstereceğiz.

Web’in görünmez taşıyıcıları olan cookie’ler (çerezler), kullanıcı deneyimini kişiselleştirmekten oturumları yönetmeye kadar birçok kritik görevi üstlenen basit metin dosyalarıdır. Neden ihtiyacımız var konusuna gelicek olursak HTTP protokolü stateless bir protokoldür. Ne demek oluyor bu stateless? Çok basit: Web sunucusu, her gelen isteği bir öncekiyle ilişkilendirmez. Yani, bir kullanıcı olarak sen az önce giriş yapmış olsan bile, sunucu seni bir sonraki istekte tanımaz. Her istek yepyeni bir misafir gibi değerlendirilir. İşte burada devreye cookie’ler giriyor. Sunucu tarafından “Set-Cookie” header belirteciyle eklenir ve her sonraki istekle birlikte otomatik olarak geri gönderilirler.

Örnek bir Set-Cookie HTTP yanıtı:

HTTP/1.1 200 OK
Content-Type: application/json
Set-Cookie: session=Y3liZXJ3aXNI; Path=/;

Şimdi Cookie’nin özelliklerine göz atmaya başlayalım. Dikkatinizi çekmem gereken bir husus var: Cookie, gönderildiği tüm özellikleri birbirinden noktalı virgül(;) ve boşluk karakteri( ) ile ayrılmaktadır.

Set-Cookie HTTP başlığında gönderilen ilk değer ve tek zorunlu alan, set edilmek istenen cookie’nin kendisi yani “Name=Value” çiftidir. Bu, tarayıcının saklayacağı asıl bilgidir. Sonrasında gelen ve noktalı virgül(;) ile ayrılan kısımlar ise bu cookie’nin nasıl davranacağını belirleyen niteliklerdir.

Websitesinde kullanılan “Name” değeri saldırgan perspektifinden bakıldığında arkada hangi teknolojiler çalıştığı hakkında bilgi verebilir. Örneğin Django frameworkü için django_sessionid varsayılan olarak kullanılır. Diğer yaygın web frameworkleri ve varsayılan cookie isimleri:

Cookie İsmi Framework / Platform
PHPSESSID PHP
JSESSIONID Java EE / Spring / JSP
ASPSESSIONID Classic ASP
ASP.NET_SessionId ASP.NET
CFID / CFTOKEN ColdFusion
zope3 Zope
cakephp CakePHP
laravel_session Laravel
ci_session CodeIgniter
PLAY_SESSION Play Framework
connect.sid Express.js (Node.js)
symfony Symfony
django_sessionid Django
session Flask (varsayılan)
sid Socket.IO
weblogic.session.se Oracle WebLogic Server

Kullandığımız browser hangi HTTP isteğine Cookie’yi ekleyip eklemeyeceğini nasıl bilebiliyor?

Bunun cevabı da hem cookie’lerin attribute’larında hem de browserın güvenlik mekanizmasında yatıyor. Attribute’lara detaylıca birazdan değineceğiz ama öncelikle browserın özelliğine bakalım. Bir cookie set edilirken “Domain=” parametresi hiç kullanılmazsa, tarayıcı o cookie’yi “Host-Only” olarak işaretler. Bu, cookie’nin SADECE ve SADECE onu set eden tam host adı (hostname) için geçerli olduğu anlamına gelir.

Bu küçük veri parçacıklarının aslında yanlış yapılandırıldığında büyük güvenlik riskleri taşır. Yanlış yapılandırılmış bir cookie hassas kullanıcı verilerinin çalınmasına, hesapların ele geçirilmesine (session hijacking), siteler arası betik çalıştırma (XSS) ve siteler arası istek sahteciliği (CSRF) gibi saldırılara kapı aralayabilir. Bu saldırıların önüne geçebilmek adına bazı güvenlik mekanizmaları geliştirilmiştir. Gelin şimdi teker teker inceleyelim.

Secure

Cookie güvenliğinin temel taşlarından biri olan Secure flagi adından da anlaşılacağı üzere, cookie’nin sadece güvenli bağlantılar yani HTTPS üzerinden iletilmesini sağlar. Bir cookie Secure bayrağı ile işaretlendiğinde, tarayıcı bu cookie’yi yalnızca web sunucusuna HTTPS bağlantısı üzerinden yapılan isteklerde gönderir. Eğer kullanıcı siteye HTTP üzerinden erişmeye çalışırsa veya bir şekilde HTTP bağlantısına yönlendirilirse, Secure olarak işaretlenmiş cookie’ler sunucuya gönderilmez.

Bu ne için önemlidir? Tabii ki “Ortadaki Adam” (Man-in-the-Middle - MitM) saldırılarını engellemek için önemlidir. Eğer cookie’ler şifresiz HTTP üzerinden gönderiliyorsa, saldırgan bu cookie’leri (özellikle oturum kimlikleri gibi hassas verileri içerenleri) kolayca ele geçirebilir. Bu durum, oturum hırsızlığına (session hijacking) ve kullanıcı hesaplarının yetkisiz erişimine yol açabilir.

Örnek kullanım şu şekildedir:

Set-Cookie: session=Y3liZXJ3aXNI; Secure; Path=/

Daha anlaşılabilir kılmak için şu şekilde örnekleyelim: OWASP Juice Shop’da bulunan hiçbir cookie’de Secure flagi işaretlenmeden atanmış bulunuyor. Bu web sitesine HTTP ve HTTPS üzerinden istek attığımızda cookieler eklenecektir.

alt text

“continueCode” ve “cookieconsent_status” cookielerine, Cookie Editor eklentisi aracılığıyla Secure flagi ekleyip hangi cookie’ler HTTP isteklerine ekleniyor BurpSuite ile araya girip bunu gözlemleyelim:

alt text

Sadece “language” ve “welcomebanner_status” cookie’leri HTTP isteğine eklendi.

HttpOnly

HttpOnly, bir cookie set edilirken Set-Cookie HTTP yanıt başlığına eklenebilen ek bir bayraktır. Temel amacı, cookie’nin istemci taraflı betikler (yani tarayıcıda çalışan JavaScript) tarafından erişilmesini engellemektir. Bu özellik, özellikle XSS saldırılarının en yaygın hedeflerinden biri olan oturum cookie’lerinin çalınmasını engellemek için geliştirilmiştir. XSS açığı aracılığıyla kullanıcının tarayıcısında zararlı bir betik çalıştırdığında, bu betik normalde document.cookie API’sini kullanarak oturum cookie’sine erişebilir ve bu bilgiyi kendi sunucusuna gönderebilir.

Sunucu tarafından tarayıcının belleğinde tutulan bir cookie, httpOnly parametresi içeriyorsa bu cookie yalnızca HTTP isteklerine eklenir ve Javascript tarafından okunamaz, manipüle edilemez.

Set-Cookie: sessionId=Y3liZXJ3aXNI; Path=/; Secure; HttpOnly

Daha anlaşılabilir kılmak için şu şekilde örnekleyelim:

Aşağıdaki görselde göründüğü üzere hiçbir cookie’de HttpOnly flagi bulunmuyor.

alt text

HttpOnly flagi içermeyen cookie’yi şu şekilde konsoldan çekmeye çalışalım:

alt text

Cookie’ye Cookie Editor eklentisiyle HttpOnly flagi ekleyip tekrardan cookie’yi Javascript aracılığıyla almayı deneyelim

alt text

“document.cookie” komutunu çalıştırdığımızda HttpOnly flagine sahip cookie’lerin gelmediğini görüyoruz.

alt text

Max-Age ve Expires

Cookie yönetiminin bir diğer kritik boyutu daha var: ömürleri. Tarayıcı bir cookie’yi ne kadar süreyle saklamalı ve sunucuya geri göndermeli? Bu sorunun cevabı, cookie’nin “kalıcı” mı yoksa yalnızca mevcut oturum süresince mi geçerli olacağını belirler. İşte bu noktada, cookie ömrünü kontrol etmek için kullanılan iki temel attribute devreye giriyor: Expires ve Max-Age. Bu iki attribute’un ne işe yaradığını, aralarındaki farkları ve hangisini ne zaman tercih etmeniz gerektiğini ele alacağız. Öncelikle Oturum Cookie’leri ve Kalıcı Cookie’ler arasındaki ayrımı yapalım:

Oturum Cookie’leri (Session Cookies): Expires veya Max-Age attribute’ları belirtilmezse, cookie bir oturum cookie’si olur. Bu tür cookie’ler tarayıcı kapatıldığında (veya tarayıcı oturumu sona erdiğinde) otomatik olarak silinir. Oturum yönetimi için sıklıkla kullanılırlar.

Kalıcı Cookie’ler (Persistent Cookies): Expires veya Max-Age attribute’larından en az biri belirtilirse, cookie kalıcı hale gelir. Tarayıcı, belirtilen süre dolana kadar veya manuel olarak silinene kadar tarayıcı kapatılıp açılsa bile bu cookie’yi saklar ve ilgili siteye yapılan isteklerle gönderir.

Örnek kullanımlar şu şekildedir:

Set-Cookie: user_preference=dark_mode; Expires=Wed, 21 Oct 2026 07:28:00 GMT
Set-Cookie: tracking_consent=accepted; Max-Age=31536000; Path=/

Bu cookie, ayarlandığı andan itibaren 31.536.000 saniye (yaklaşık 1 yıl) boyunca geçerli olacaktır.

Eğer bir Set-Cookie başlığında hem Expires hem de Max-Age belirtilirse, modern tarayıcılar Max-Age değerini dikkate alır ve Expires’i görmezden gelir. Eski tarayıcılar (özellikle IE 6, 7, 8) Max-Age’i desteklemediği için yalnızca Expires’i kullanırdı, bu yüzden bazen her ikisi de uyumluluk için eklenebiliyordu, ancak günümüzde bu genellikle gereksizdir.

Modern web geliştirmede, kalıcı cookie’ler için Max-Age kullanmak genellikle en iyi yoldur.

Domain

Domain, cookie’lerin belli subdomainlerde kullanılmasını sıkılaştırmak amacıyla çok kullanışlı bir özelliktir. Yukarıda bahsettiğimiz üzere browserın belleğindeki cookielerin domain alanlarında bir “tail-matching” denilen bir eşleşme işlemi gerçekleştiriliyor. Domain attribute’unun nasıl işlediğini şu örnekle inceleyelim:

Diyelim ki elimizde bir alışveriş sitesi var.

  • Ana site: example.com
  • Ürün alt alanı: shop.example.com
  • Kullanıcı alt alanı: user.example.com

Kullanıcı example.com üzerinden giriş yaptıktan sonra, bu oturum bilgisini shop.example.com ve user.example.com gibi subdomainlerde de kullanmak istiyorsan, cookieler şu şekilde ayarlanmalı:

Set-Cookie: session=Y3liZXJ3aXNI; Domain=example.com; Path=/

Eğer Domain attribute’unu hiç belirtmeseydik, çerez yalnızca çerezi ayarlayan alan adıyla sınırlı olurdu. Örneğin login.example.com üzerinden kullanıcı giriş yaptı ve şu şekilde bir cookie atandı:

Set-Cookie: session=Y3liZXJ3aXNI; Path=/

Burada Domain attribute’u belirtilmediği için kullanıcımız shop.example.com‘a gittiğinde, tarayıcı bu cookie’yi shop.example.com sunucusuna göndermez, çünkü cookie’nin domain kapsamı (login.example.com) isteğin yapıldığı domain (shop.example.com) ile eşleşmez.

Güvenlik açısından dikkat etilmesi gereken hususlar:

  1. En Az Ayrıcalık Prensibi (Principle of Least Privilege): Eğer bir cookie yalnızca api.example.com tarafından kullanılacaksa, onu api.example.com‘dan Domain attribute’u belirtmeden ayarlayın. Böylece diğer subdomain’lere cookie gönderilmez.
  2. Subdomain Güvenliği: Güvenilmeyen alt alanlar varsa, Domain özniteliğini ayarlamamak daha güvenlidir. Özellikle saldırgan bir alt alan kontrolündeyse, Domain=example.com ile atanan cookie değeri ile diğer subdomainlere yönelik saldırılar düzenlenebilir.
  3. Diğer Attribute’larla Birlikte Kullanım: Domain attribute’u tek başına yeterli değildir. Güvenliği artırmak için mutlaka “Secure”, “HttpOnly” ve “SameSite” attribute’ları ile birlikte kullanılmalıdır.

Path

Path attribute, çerezin hangi alt dizinlerde (yani URI path’lerinde) geçerli olduğunu belirler. Browserlar, Domain parametresinden sonra Path parametresini kontrol ederler ve eğer uyumluluk sağlanırsa Cookie isteklere eklenir. Detaylarını şu şekilde inceleyelim:

Eğer Path attribute’u açıkça belirtilirse (örneğin, Path=/admin), cookie yalnızca belirtilen yol ve onun alt yollarına yapılan isteklere eklenir:

Set-Cookie: session=Y3liZXJ3aXNI; Domain=example.com; Path=/admin
Path Eklenir mi?
/admin/users ✅ Evet
/admin/settings/security ✅ Evet
/ (Ana dizin) ❌ Hayır
/shop ❌ Hayır

Eğer Set-Cookie başlığında Path attribute’u belirtilmezse, tarayıcı varsayılan olarak cookie’yi ayarlayan kaynağın (URL’nin) dizinini (directory) Path olarak kabul eder. Örnek HTTP request response akışı:

POST /auth/process-login HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Cookie: some_other_non_session_cookie=value

username=testuser&password=secret123
HTTP/1.1 302 Found
Location: /dashboard  <-- Başarılı login sonrası yönlendirme adresi
Set-Cookie: session_id=Y3liZXJ3aXNI

Bu yanıtı alan tarayıcı session_id cookie’sini saklarken, Path attribute’u belirtilmediği için, cookie’yi ayarlayan URL’nin (/auth/process-login) dizin yolunu (/auth/) varsayılan Path olarak kabul eder. Sonuç olarak, https://example.com/auth/user-settings gibi /auth/ altında bir yola istek yaptığında session_id cookie’sini gönderecektir. Ancak, sunucunun yönlendirdiği https://example.com/dashboard adresine veya https://example.com/ ana sayfasına ya da https://example.com/shop gibi farklı bir dizine istek yaptığında, bu session_id cookie’sini göndermeyecektir, çünkü bu yollar cookie’nin varsayılan olarak atanan Path=/auth/ kapsamına girmez. Şu tablo ile görselleştirelim:

URI Eklenir mi?
https://example.com/auth/user-settings ✅ Evet
https://example.com/dashboard ❌ Hayır
https://example.com/shop ❌ Hayır

SameSite

“SameSite” attribute’u Chrome 51 ile eklenen CSRF (Siteler arası istek sahteciliği) saldırılarını engellemeye yönelik geliştirilmiştir. Bu attribute tarayıcıya bir cookie’nin yalnızca cookie’nin ait olduğu site içinden yapılan isteklerle mi, yoksa farklı bir siteden (cross-site) başlatılan isteklerle de mi gönderileceğini söyler. Temel amacı, kullanıcının haberi olmadan, kötü niyetli bir site tarafından kullanıcının cookie’lerini kullanarak başka bir sitede işlem yapılmasını engellemektir. Bu kurallar üçüncü kaynaktan yüklenen CSS ve Javascript içeriklerine de aynen uygulanır.

SameSite attribute’unun çalışma mantığı şu üç temel değere bağlıdır ve bu değerler atanabilir:

  1. Strict: a. İsminden de kendini ele verdiği üzere en sıkı atanabilecek değer budur ve cookie yalnızca isteğin kaynağı cookie’nin ait olduğu site ile tamamen aynı olduğunda gönderilir, üçüncül hiçbir websitesine cookie eklemez.
  2. Lax: a. Cookie, aynı siteden yapılan tüm isteklerle gönderilir. Farklı bir siteden yapılan isteklerde ise yalnızca top-level navigasyona (yani browser adres çubuğunda bir değişiklik oluyorsa) sebep olacak bir GET isteğinde eklenir. Daha detaylı açıklamak gerekirse kullanıcı “another-site.com”daki bir linke tıklayarak “my-site.com”a geldiğinde (bu bir GET isteğidir), Lax cookie’si gönderilir ve kullanıcı oturumu açık kalır. Ancak “another-site.com”un arka planda “my-site.com”a POST isteği yapması veya <img>, <iframe> gibi alt kaynak istekleri ile cookie’yi göndermeye çalışması durumunda cookie gönderilmez.
  3. None: a. None ise, tüm üçüncül isteklere Cookie eklenecektir. None kullanmak için mutlaka birlikte “Secure” attribute eklenmek zorundadır. Tarayıcılar, Secure olmadan SameSite=None olarak işaretlenmiş cookie’leri reddeder. Bu, cookie’nin yalnızca şifreli (HTTPS) bağlantılar üzerinden gönderilmesini garanti altına alarak ortadaki adam saldırı riskini azaltır.

Özetle şu şekilde bir tablo ortaya çıkıyor:

İstek Tipi Örnek Kod Cookie’nin Gönderileceği Değer
Link <a href="..."> Normal, Lax
Prerender <link rel="prerender" href="..."/> Normal, Lax
Form GET <form method="GET" action="..."> Normal, Lax
Form POST <form method="POST" action="..."> Normal
Iframe <iframe src="..."></iframe> Normal
Ajax $.get("...") Normal
Image <img src="..."> Normal

__Secure- ve __Host-

Şimdiye kadar Domain, Path, Secure, HttpOnly, SameSite, Max-Age gibi temel attribute’ların önemini ve nasıl çalıştıklarını detaylıca inceledik. Bu attribute’lar, cookie’lerimizi korumak için temel yapı taşlarını oluşturur. Fakat web’in karmaşık doğası ve gelişen tehditler, bazen ek savunma hatlarına ihtiyaç duyar. İşte bu noktada isimleriyle bile dosta güven düşmana korku veren Cookie Prefix’leri devreye giriyor: __Secure- ve __Host-

Standart attribute’lar harika olsa da, bazı senaryolarda zafiyetler ortaya çıkabilir. Eğer bir uygulamanın daha az güvenli bir subdomain’i (örn. insecure-sub.example.com) varsa, bu subdomain ana domain veya diğer güvenli subdomain’ler için cookie ayarlayabilir (Domain=example.com kullanarak). Bu, ana uygulamadaki oturum cookie’lerinin üzerine yazılmasına veya manipüle edilmesine (Session Fixation gibi) yol açabilir.

Tarayıcı geliştiricileri, bu tür sorunlara karşı ek bir güvence sağlamak için cookie isimlerine özel anlamlar yükleyen iki prefix tanımladılar. Tarayıcılar bu prefix’lerle başlayan isimlere sahip cookie’leri yalnızca belirli koşullar sağlandığında kabul eder. Eğer koşullar sağlanmazsa, tarayıcı Set-Cookie başlığını sessizce reddeder ve cookie’yi saklamaz.

__Secure-” için Kural: “__Secure-” prefix’i ile başlayan bir cookie (örneğin __Secure-SessionID), Set-Cookie başlığında mutlaka Secure attribute’u ile birlikte gönderilmelidir.

// DOĞRU KULLANIM: Tarayıcı kabul eder.
Set-Cookie: __Secure-SID=12345; Secure; Path=/; HttpOnly; SameSite=Lax

// YANLIŞ KULLANIM: Tarayıcı reddeder (Secure attribute'u eksik).
Set-Cookie: __Secure-SID=67890; Path=/; HttpOnly; SameSite=Lax

__Host-” için şu kurallar yerine getirilmeli:

  1. Secure attribute’u bulunmalıdır.
  2. Path attribute’u “/” olarak ayarlanmalıdır.
  3. Domain attribute’u belirtilmemelidir.
// DOĞRU KULLANIM:
Set-Cookie: __Host-SID=abcdef; Secure; Path=/; HttpOnly; SameSite=Lax

// YANLIŞ KULLANIM: Tarayıcı reddeder (Domain attribute'u var).
Set-Cookie: __Host-SID=ghijkl; Secure; Path=/; Domain=example.com; HttpOnly; SameSite=Lax

// YANLIŞ KULLANIM: Tarayıcı reddeder (Path=/ değil).
Set-Cookie: __Host-SID=mnopqr; Secure; Path=/users; HttpOnly; SameSite=Lax

// YANLIŞ KULLANIM: Tarayıcı reddeder (Secure attribute'u eksik).
Set-Cookie: __Host-SID=stuvwx; Path=/; HttpOnly; SameSite=Lax

Belki bütün bunlar ilk bakışta çok fazla detay gibi görünebilir, ancak unutmayın ki bu ayarları doğru yapılandırmak, XSS, CSRF ve oturum kaçırma (session hijacking) gibi yaygın saldırılara karşı en önemli savunma hatlarımızdan biri. Bu attribute’ları, web uygulamalarınız için birer güvenlik kalkanı gibi düşünebilirsiniz.

Umarım bu yazı, cookie güvenliği konusundaki farkındalığınızı artırmış ve uygulamalarınızı daha güvenli hale getirme yolculuğunuzda size rehberlik etmiştir.

Güvenli kodlamalar!

Written by

Yusuf Yıldız