JWT Güvenli Derken Güvenlik Açığı Oluşturmayın
Yazan Evren BalYayın tarihi Güncellendi · 7 dk okuma

Sayfayı kopyala
💡 Özet (TL;DR):
- Sorun: JWT (JSON Web Token) sunucu tarafında oturum (session) bilgisi tutmadığında, şifre değişikliği veya çıkıştan sonra önceden üretilmiş token'ları tek tek iptal etmek için ek bir mekanizma gerekir.
- Çö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şturmak için kullanılan bir standarttır. Bu token genellikle sunucu tarafında oluşturularak istemciye gönderilir. İmzalı bir JWT'nin bütünlüğü, HMAC gibi paylaşılan bir sırla veya RSA/ECDSA gibi asimetrik bir imzayla korunabilir. İkinci durumda sunucu özel anahtarla imzalar, karşı taraf açık anahtarla doğrular. Yani her JWT özel anahtarla imzalanmaz.

İmza doğrulanıyorsa, token içeriğini yetkisiz birinin değiştirmediğini ve onu ilgili anahtara sahip tarafın ürettiğini anlarız. Ancak imzalı JWT şifrelenmiş değildir. Payload içindeki bilgileri görebilen herkes okuyabilir; bu yüzden token'a gizli veri koymamak gerekir. İmza, "kullaniciadi": "evrenbal" bilgisinin bir noktada bizim sistemimiz tarafından yazıldığını söyler. Kullanıcının hâlâ aktif olup olmadığını, yetkisinin değişip değişmediğini veya bu istekte o kaynağa erişip erişemeyeceğini tek başına söylemez.
JWT Ne Değildir?
Sanılanın aksine, bilinçli uygulanmadığında JWT tek başına güvenli değildir. Kritik bir istekte imzayı doğrulamak, uygulamanın kendi yetkilendirme kararının yerine geçmez.
Şö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 token'ı doğrularken veritabanından şifre, hesap durumu veya oturum kontrolü yapmıyorsanız token'dan "evrenbal" kullanıcı adı gelmeye devam eder. Süresi dolana kadar yetkisiz kişi sisteme erişmeyi sürdürebilir.
Bu noktada iki ayrı kontrolü birbirine karıştırmamak gerekir. Birincisi kriptografik doğrulama, ikincisi uygulamanın kararlarıdır. JWT'yi kabul eden kod, token başlığının seçtiği algoritmaya güvenmemeli; uygulamanın izin verdiği algoritma ve o algoritmaya bağlı anahtarla doğrulama yapmalıdır. İmzalama ya da şifreleme gibi kullanılan her kriptografik işlem doğrulanmalı; issuer, audience, amaç ve gerekiyorsa token tipi beklenen değerlerle eşleşmelidir. JWT için güncel IETF uygulama önerileri tam olarak bu kontrolleri ister. Ardından uygulama, gelen claim'leri kendi bağlamında doğrular ve güncel yetki kararını verir.
"JWT'nin geçerlilik süresini ayarlayabiliyorum. Kısa bir süre belirlerim, olur biter."
JWT'nin geçerlilik süresini kısa tutup, daha uzun süreli bir "Refresh Token" oluşturmak iyi bir seçenek olabilir. Fakat refresh token dağıtmak da otomatik bir varsayılan olmamalı; istemci tipi ve çalınma riskine göre karar verilmelidir.
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. Public client'larda token rotation veya istemci örneğine kriptografik olarak bağlı (sender-constrained) token yaklaşımı, çalınan refresh token'ın tekrar kullanılmasını fark etmek için güncel öneridir. Süre sonu ve iptal akışı da tasarımın parçası olmalıdır. OAuth 2.0 güvenlik önerileri bu tercihin risk değerlendirmesiyle yapılmasını ve public client'larda bu iki yaklaşımdan birini öneriyor.

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 | Token istemcide taşınır; tarayıcıda localStorage veya cookie gibi seçimler ayrı bir güvenlik kararıdır. | Session kaydı sunucuda (bellek, veritabanı, Redis) tutulur; tarayıcı çoğunlukla kısa bir session ID taşır. |
| Durum Bilgisi | Stateless (Sunucu durum kaydı tutmaz). | Stateful (Sunucu kimin giriş yaptığını takip eder). |
| İptal Etme (Revocation) | Denylist, introspection veya sunucu tarafında oturum durumu kontrolü ile iptal edilebilir; bunun için state gerekir. | Sunucudan session silinerek anında iptal edilebilir. |
| Ölçeklenebilirlik | Yerel imza doğrulaması sorguyu azaltabilir. Bu avantaj her mimaride gerekli veya anlamlı değildir. | 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. IP adresi, işletim sistemi ve tarayıcı bilgisi gibi ayrıntılar oturum listesini anlamlandırmak veya şüpheli değişimleri fark etmek için yardımcı sinyal olabilir. Bunları kriptografik cihaz bağlama ya da tek başına güvenilir kimlik kanıtı saymamak gerekir.
İ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.
Tarayıcıda token taşımak da ayrı bir karar
JWT'yi localStorage içinde saklamak, token'ı aynı origin'de çalışan JavaScript'in okuyabileceği hâle getirir. XSS oluşursa bunun maliyeti büyür. HttpOnly, Secure ve uygun SameSite nitelikleri olan cookie'ler veya tarayıcı ile token'ın arasına BFF koyan bir mimari bu maruziyeti azaltabilir. Ancak cookie tarayıcı tarafından otomatik gönderildiği için CSRF ve session tasarımını ayrıca ele almak gerekir. SameSite yardımcı bir savunmadır, CSRF korumasının tamamı değildir. OWASP oturum yönetimi rehberi bu sınırları açıkça anlatıyor.
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 ayrım, AI ile API geliştirirken de prompta bırakılacak bir ayrıntı değil. Bir agent'ın JWT'yi varsayılan çözüm seçmemesi gerekir. İstemci türünü, iptal ihtiyacını, güven sınırını ve JWT'nin sunucu oturumundan neden daha iyi olduğunu açıklamasını istemek daha doğru başlangıçtır. Bu kararların API kalitesini nasıl koruduğunu ayrıca Yapay Zekâ ile API Geliştirirken Kaliteyi Korumak yazısında anlattım.
Bu yazıyı faydalı bulduysanız
Web sitenizde ilgili bir içerikten bu yazıya bağlantı vermeniz ya da sosyal medyada paylaşmanız, daha fazla kişiye ulaşmasına gerçekten yardımcı olur. Desteğiniz için teşekkür ederim.
Bağlantı ve marka kullanım rehberi →Bu yazı hakkında
Değişiklik geçmişi
- · Açıklama — Yazı özeti düzenlendi.
- · Esaslı güncelleme — İmla ve terim hataları düzeltildi, stateless/stateful farklarını özetleyen karşılaştırma tablosu ile TL;DR paneli eklendi.
- · Esaslı güncelleme — İmza, claim doğrulama, refresh token, iptal, tarayıcı depolaması ve cihaz sinyallerine ilişkin güvenlik açıklamaları güncellendi.
