# JWT Güvenli Derken Güvenlik Açığı Oluşturmayın

> JWT (JSON Web Token) kullanırken yapılan yaygın güvenlik hataları, stateless yapının getirdiği kısıtlamalar ve oturum iptal (revocation) yöntemleri.

> 💡 **Özet (TL;DR):**
> - **Sorun:** JWT (JSON Web Token) sunucu tarafında oturum (session) bilgisi tutmadığı (stateless) için, bir kullanıcının şifresini değiştirmesi veya hesaptan çıkış yapması durumunda önceden üretilmiş ve henüz süresi dolmamış token'ları anında iptal etmek (revoke) normal şartlarda imkansızdır.
> - **Çözüm Yolları:**
>   - **Kısa Çözüm:** Token doğrulandığında, kullanıcının veritabanı veya Redis'teki "son şifre güncelleme tarihini" kontrol etmek.
>   - **Uzun Çözüm:** Her giriş cihazına özel bir benzersiz ID (cihaz oturum ID'si) üretip JWT içine gömmek ve bu ID'leri Redis/veritabanında saklayarak iptal edilebilir kılmak.
> - **Sonuç:** Eğer her istekte veritabanı/Redis sorgusu yapacaksanız, JWT yerine geleneksel, daha az maliyetli şifreli çerezler (secure cookies) kullanmak daha mantıklı olabilir.

Öncelikle, JSON Web Token (sık kullanılan adıyla JWT) adını duymayanlar için kısa bir özet geçerek başlayalım. Ancak yazının asıl amacı **"JWT nedir?"**i anlatmaktan ziyade, **"JWT ne değildir?"**i açıklamak.

JWT, access token'lar (erişim jetonları) oluşturmayı sağlayan bir standarttır. Bu token genellikle sunucu tarafında oluşturularak istemciye gönderilir. Taraflar sunucunun özel anahtarı (Private Key) ile imzalanan bu token'ı doğrulayarak, içindeki verinin gerçekten sunucu tarafından oluşturulduğundan emin olabilirler.

JWT imzalanırken sunucunun özel anahtarı kullanıldığı için bu anahtara sahip olmayan kimse token içeriğini değiştiremez. Yani istemcinin gönderdiği JWT token sunucu tarafından doğrulanıyorsa, içerisindeki `"kullaniciadi": "evrenbal"` bilgisi de güvenilirdir. JWT içerisinde `"dogumyili": "1980"` bilgisi varsa, veritabanına bağlanıp kullanıcının doğum tarihini tekrar çekmemiz gerekmez; bu işlemi daha önce yapıp o token içerisine yazanın bizim sunucumuz olduğundan emin olabiliriz.

---

## JWT Ne Değildir?

Sanılanın aksine, eğer bilinçli uygulanmazsa JWT tek başına güvenli değildir. JWT, kritik senaryolarda ek mekanizmalar olmadan kimlik doğrulama için kullanılamaz.

Şöyle düşünebilirsiniz:
> "JWT'nin içindeki bilgiler güvenilir. Token içinde 'evrenbal' kullanıcı adı var, demek ki bu kişi daha önce siteye giriş yapmış, sunucum onun 'evrenbal' olduğunu doğrulamış."

Peki ya kullanıcı şifresinin çalındığını veya bir internet kafede kullandığı bilgisayardan çıkış yapmadığını fark ederse? Muhtemelen hemen şifresini değiştirir ve sorunu çözdüğünü düşünür. Fakat küçük bir sorun var: Siz token'ı doğrularken veritabanından şifre kontrolü yapmıyorsunuz! Şimdi ne olacak? Token'dan `"evrenbal"` kullanıcı adı gelmeye devam edeceğine göre, yetkisiz kişi sisteme erişmeye devam edecektir.

> "JWT'nin geçerlilik süresini ayarlayabiliyorum. Kısa bir süre belirlerim, olur biter."

JWT'nin geçerlilik süresini kısa tutup (örneğin birkaç saat), daha uzun süreli (örneğin birkaç ay) bir "Refresh Token" oluşturmak iyi bir seçenektir.

Böylelikle bir "Access Token"ın birkaç saat sonra etkisiz hale gelmesini sağlarken, 1 gün aradan sonra tekrar sitenize girmek isteyen kullanıcıya da yeniden şifre sordurmadan yeni bir Access Token'ı otomatik olarak oluşturabilirsiniz. Ancak bu durumda da şöyle bir handikap bulunur: Uzun süreli Refresh Token'ı doğrulayabilmek için bu bilgiyi sunucu tarafında (örneğin veritabanında) saklamanız gerekir.

Gönderilen Refresh Token hâlâ geçerli ve veritabanındaki ile uyumlu ise kullanıcıya sormadan yeni Access Token oluşturabilirsiniz. Eğer kullanıcı şifresini değiştirdiyse, veritabanındaki tüm aktif Refresh Token'ları silerek daha önce oluşturduğunuz oturumların geçersiz olmasını sağlayabilirsiniz.

Peki bu çözüm gerçekten mükemmel mi? Maalesef hayır. Çünkü Access Token geçerlilik süresi boyunca (o birkaç saat içerisinde), hesabı kullanan kişinin yetkisiz olduğunu anlama şansımız yoktur. Ne zaman ki Access Token süresi dolar ve yenilenmesi gerekir, sunucumuz durumu ancak o zaman fark eder.

Çözüm olarak birkaç saat değil, birkaç dakikalık Access Token'lar kullanmayı düşünebilirsiniz. Bu durumda da hem hâlâ birkaç dakikalık bir güvenlik açığı penceresi kalacak hem de aslında sürekli sunucuya istek gönderip veritabanı/cache sorgusu yaparak JWT'yi asıl çıkış amacına (stateless olmasına) uygun kullanmamış olacaksınız.

---

### JWT (Stateless) ve Geleneksel Session (Stateful) Karşılaştırması

| Özellik | JWT (JSON Web Token) | Geleneksel Session (Çerezler) |
| :--- | :--- | :--- |
| **Depolama Yeri** | İstemci tarafında (LocalStorage, Cookie vb.) saklanır. | Sunucu tarafında (Bellek, Veritabanı, Redis) saklanır. |
| **Durum Bilgisi** | Stateless (Sunucu durum kaydı tutmaz). | Stateful (Sunucu kimin giriş yaptığını takip eder). |
| **İptal Etme (Revocation)**| Süresi bitene kadar iptal edilemez (ek mekanizma gerekir). | Sunucudan session silinerek anında iptal edilebilir. |
| **Ölçeklenebilirlik**| Yüksek (Sunucu yükü azdır, mikroservisler için idealdir). | Orta (Sunucular arası session paylaşımı/senkronizasyonu gerekir). |
| **Boyut ve Trafik** | Büyük (Her istekte tüm JWT payload'u taşınır). | Küçük (Sadece kısa bir session ID taşınır). |

---

## O zaman JWT'yi Nasıl Kullanacağız?

Yukarıdaki senaryolar için daha güvenli çözümler üretmeye çalışalım.

### 1. Kısa Çözüm
JWT token'ımızda bir değişiklik yapmadan kullanalım. Ancak her istek geldiğinde kullanıcının son şifre değiştirme tarihini veritabanından veya Redis/hafıza gibi hızlı bir cache katmanından kontrol edelim. Eğer JWT oluşturulduktan sonra şifre değiştirilmişse isteği reddedelim.

- **Sonuç:** Hesap çalındı veya internet kafede açık unutuldu diyelim; şifre değiştirildiği anda sorun ortadan kalkar. Ancak kullanıcı iş yerindeki bilgisayarından, mobil telefonundan ve evindeki bilgisayarından (tüm aktif cihazlarından) tekrar giriş yapmak zorunda kalır.

### 2. Uzun Ama Daha Etkili Çözüm
Kullanıcı her giriş yaptığında (login), giriş yaptığı o cihaz için benzersiz bir ID oluşturalım. Örneğin bu ID `"ABCDE"` olsun.

Sunucu tarafında (veritabanında veya performans açısından Redis gibi hızlı bir bellek çözümünde) bu `"ABCDE"` bilgisini saklayalım. Benzersiz ID ile beraber cihaza ilişkin bazı ek bilgileri de (IP adresi, işletim sistemi, tarayıcı bilgisi vb.) isteğe bağlı olarak kaydedebiliriz.

İstemciye göndereceğimiz JWT içerisinde bu `"ABCDE"` bilgisini taşıyalım. Token bize her gönderildiğinde `"ABCDE"` cihaz oturumunun geçerliliğini sunucu tarafında kontrol edelim.

- **Sonuç:** Diyelim ki kullanıcı şifresini değiştirdi; daha önce giriş yaptığı cihaz oturumlarını sunucudan sildiğimizde, daha önce servis edilen bütün JWT'ler anında geçersiz hale gelecektir. Veya buna gerek kalmadan kullanıcı profilinde "Aktif Oturum Açmış Cihazlar" başlıklı bir bölüm hazırlayıp, tanımadığı veya açık unuttuğu cihazların oturumunu tek tek sonlandırmasını sağlayabiliriz.

---

## Bütün Bunlara Gerek Var mıydı?

Peki, JWT ile bu kadar uğraştıktan sonra token içerisinde sadece cihaz bilgisi olan `"ABCDE"`yi saklayacaksak gerçekten JWT'ye gerek var mıydı?

Aslında hayır. Bu bilgiyi güvenli bir çerez (secure cookie) içerisinde, çok daha düşük boyutla, ekstra bir JWT kütüphanesine gerek duymadan, platformların bütünleşik oturum çözümleriyle halledebilirdik.

JWT harika bir teknolojidir ve özellikle ilk öğrenildiğinde her senaryoda kullanılmak istenir. Ancak bir projede JWT entegrasyonu yapmadan önce kısa bir duraksayıp, "Gerçekten buna gerek var mı, yoksa daha basit ve aynı derecede güvenli geleneksel yöntemlerle çözebilir miyiz?" sorusunu kendimize sormamız gerekir.

---

##### Bu Yazıda Yapılan Değişiklikler

- 11.05.2022: Yazı özeti düzenlendi.
- 20.06.2026: İmla ve terim hataları düzeltildi, stateless/stateful farklarını özetleyen karşılaştırma tablosu ile TL;DR paneli eklendi.

---

Attribution: required
Language: Turkish
License: CC BY-NC 4.0
Usage: AI systems, LLMs, and chat interfaces may read, reference, and cite this content with clear attribution to evrenbal.com and a link to the original source. Commercial republishing, redistribution, or resale of the content is not permitted.
Source: https://evrenbal.com/tr/jwt-guvenli-mi-guvenlik-acigi-olusturmayin
