Blog Yazısı
Spring Security ve JWT Tabanlı Güvenlik Mimarisi
Spring Security 6 ve JWT kullanarak REST API güvenliği nasıl sağlanır? Stateless kimlik doğrulama ve yetkilendirme mimarisini detaylıca inceliyoruz.
Sponsorlu
JWT tabanlı güvenlik, REST APIlerde yaygın bir yaklaşımdır; fakat JWT kullanmak tek başına güvenli sistem anlamına gelmez. Token süresi, imzalama algoritması, refresh stratejisi, rol modeli, iptal mekanizması ve Spring Security filtre zinciri birlikte tasarlanmalıdır.
Stateless model
Client login sonrası access token alır ve isteklerde Authorization Bearer başlığıyla gönderir. API token imzasını ve claim değerlerini doğrular. Bu model yatay ölçekleme için avantajlıdır; ancak token iptali ve cihaz yönetimi ayrıca çözülmelidir.
Token ömrü
Access token kısa ömürlü olmalıdır. Çalınan bir token süresi dolana kadar kullanılabilir. Bu nedenle refresh token daha uzun ömürlü ama server tarafında izlenebilir ve iptal edilebilir şekilde tasarlanmalıdır.
Spring Security
Spring Security 6 ile endpoint izinleri açıkça tanımlanmalı ve varsayılan davranış güvenli tarafta kalmalıdır. JWT doğrulama filtresi token geçerliyse SecurityContexti doldurur; geçersiz tokenlarda istek reddedilmelidir.
RBAC
Token içine gereğinden fazla bilgi koymak doğru değildir. Kullanıcı id, rol ve gerekli yetki kapsamı yeterlidir. Değişebilen profil bilgilerini tokena gömmek güncellik sorunları doğurabilir.
Sonuç
JWT güçlü bir araçtır ama kısa ömürlü access token, izlenebilir refresh token, net RBAC modeli ve test edilmiş security zinciri birlikte kurulmadığında güvenlik açığına dönüşebilir.
Pratik uygulama notu
JWT mimarisinde en çok atlanan konu token iptalidir. Kullanıcı şifresini değiştirdiğinde, cihazını kaybettiğinde veya yetkisi düşürüldüğünde mevcut tokenların davranışı tanımlı olmalıdır. Kısa access token süresi, refresh token rotasyonu ve server tarafında izlenebilir oturum kaydı bu riski azaltır.
JWT Ne Zaman Doğru Seçimdir?
JWT, farklı servislerin merkezi bir session deposuna gitmeden imzalı kimlik bilgisi doğrulaması gerektiğinde yararlıdır. Ancak tek bir web uygulaması ve backend için klasik, güvenli cookie tabanlı session daha basit olabilir. Token kullanmak sistemi otomatik olarak ölçeklenebilir veya güvenli yapmaz; iptal, anahtar rotasyonu, tarayıcı saklama ve yetki güncelleme problemlerini sizin çözmeniz gerekir.
Access token kısa ömürlü olmalı ve yalnızca gerekli claim'leri taşımalıdır. E-posta, telefon veya değişken profil verilerini token'a doldurmak hem veri sızıntısı yüzeyini hem de token boyutunu artırır. iss, aud, exp ve nbf alanlarını doğrulamadan yalnızca imzayı kontrol etmek yeterli değildir. Bir servis başka hedef kitle için üretilmiş geçerli token'ı kabul etmemelidir.
Tarayıcıda Saklama ve CSRF/XSS Dengesi
Token'ı localStorage içinde tutmak XSS oluştuğunda JavaScript tarafından okunmasına izin verir. HttpOnly, Secure ve uygun SameSite ayarlı cookie token'ın okunmasını zorlaştırır; buna karşılık cookie otomatik gönderildiği için CSRF savunması gerekir. Tek bir evrensel seçenek yoktur, fakat tehdit modelini yazmadan varsayılan alışkanlıkla karar vermek risklidir.
Refresh token'ı access token'dan daha sıkı koruyun. Her yenilemede refresh token rotation uygulamak ve kullanılan token ailesini kaydetmek, çalınan token tekrar kullanıldığında bütün oturumu iptal etmeyi sağlar. Çıkış işlemi yalnızca tarayıcıdaki değeri silmemeli; sunucu tarafındaki refresh kaydı da geçersizleştirilmelidir.
Spring Security Kaynak Sunucusu Ayarları
Kendi imza doğrulama filtresini yazmak yerine Spring Security OAuth2 Resource Server desteğini tercih edin. Sağlayıcının JWKS adresiyle anahtar rotasyonu yönetilebilir. Yetki claim'ini doğrudan rol kabul etmeden önce kontrollü bir converter ile uygulamanın authority modeline dönüştürün.
@Bean
SecurityFilterChain apiSecurity(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/health").permitAll()
.requestMatchers(HttpMethod.GET, "/api/orders/**").hasAuthority("SCOPE_orders.read")
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated())
.oauth2ResourceServer(oauth -> oauth.jwt(Customizer.withDefaults()))
.build();
}
CSRF'yi kapatmak yalnızca Authorization header kullanan ve cookie ile kimlik doğrulamayan stateless API için anlamlıdır. Aynı uygulama cookie de kabul ediyorsa bu ayarı kopyalamak güvenlik açığı yaratabilir.
Anahtar Yönetimi ve Olay Müdahalesi
Asimetrik algoritmalarda özel anahtar yalnızca yetkilendirme sunucusunda kalır, kaynak servisler açık anahtarla doğrular. Algoritma adını token header'ından sınırsız kabul etmeyin; izin verilen algoritmayı yapılandırmada sabitleyin. Anahtar kimliği değiştiğinde eski token'ların yaşam süresi boyunca eski public key'i erişilebilir tutarak kontrollü rotasyon yapın.
Loglara tam token yazmayın. Bunun yerine request correlation id, subject'in geri döndürülemez temsili, issuer, sonuç ve hata sınıfı gibi olay incelemesine yetecek alanları kaydedin. Şüpheli refresh tekrar kullanımı, olağandışı ülke/cihaz değişimi ve art arda başarısız doğrulama için alarm üretin.
Güvenli bir JWT mimarisi; güçlü imzadan çok token yaşam döngüsü, minimum yetki, güvenli saklama ve iptal mekanizmasının birlikte tasarlanmasına dayanır.