JWT Kullanmayı Bırakın
(gist.github.com/samsch)- JWT, kullanıcıyı oturum açmış durumda tutmak için uygun değildir; bu amaç için normal çerez oturumu daha uygundur
- JWT spesifikasyonu, yaklaşık 5 dakika veya daha kısa ömürlü kısa süreli token varsayımıyla tasarlanmıştır; oturumların ise bundan daha uzun ömürlü olması gerekir
- Güvenli durumsuz kimlik doğrulamayı gerçekleştirmek zordur ve token’ları güvenli şekilde ele almak için sonuçta bir miktar durum saklama alanı gerekir
- Yalnızca basit oturum token’ı taşıyan JWT’ler, normal oturum çerezlerinden daha verimsiz ve daha az esnektir; ayrıca kimlik doğrulama bilgilerinin localStorage veya sessionStorage içinde saklanmaması gerekir
- Kısa ömürlü imzalı token’lara ihtiyaç duyulduğunda, güvenlik için tasarlanmış PASETO daha iyi bir seçimdir; ancak oturum amacıyla kullanılmamalıdır
Temel özet
- JWT, kullanıcıyı oturum açmış durumda tutmak için kullanılmamalıdır; bu amaç için normal çerez oturumları daha iyi bir araçtır
- JWT bu amaç için tasarlanmamıştır ve güvenli değildir; oturum açma oturumlarını sürdürmek için standart çerez oturumları daha uygundur
- İlgili bir konu olarak, JWT token’ları da dahil olmak üzere kimlik doğrulama bilgileri localStorage veya sessionStorage içinde saklanmamalıdır
- Konuyla ilgili sunumlar izlenebilir, ancak CSRF koruması gibi diğer başlıklar genelde kısaca ele alındığı için bunların ayrıca başka kaynaklardan öğrenilmesi gerekir
- Videonun son kısmındaki “geçerli” JWT kullanım senaryoları da daha iyi ve daha güvenli araçlarla kolayca çözülebilir; özellikle PASETO buna karşılık gelir
JWT’den neden kaçınılmalı
- JWT spesifikasyonu, yalnızca yaklaşık 5 dakika veya daha kısa ömürlü çok kısa süreli token’lar için tasarlanmıştır; oturumların ise bundan daha uzun ömürlü olması gerekir
- Güvenli bir durumsuz kimlik doğrulama biçimi mümkün değildir; token’ları güvenli şekilde işlemek için mutlaka bir miktar durum gerekir
- Bir veri deposu gerekecekse, yalnızca token durumunun bir kısmını yönetmek yerine tüm veriyi saklamak daha iyidir
- İlgili sorunlar http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/ adresinde daha ayrıntılı ele alınır
- Gerçekte JWT’yi bu şekilde kullanan uygulamalar vardır, ancak bu uygulamalar kusurludur; bu yüzden aynı hatayı tekrarlamamak gerekir
- Sadece basit bir oturum token’ı saklayan JWT’ler, normal oturum çerezlerinden daha verimsiz ve daha az esnektir; ek bir fayda da sağlamaz
- JWT spesifikasyonunun kendisi güvenlik uzmanları tarafından güvenilir bulunmadığından, güvenlik ve kimlik doğrulama ile ilgili tüm kullanım alanlarından dışlanmalıdır
- İlk spesifikasyon sahte token üretimine izin veriyordu ve başka hatalar da içeriyor olabilir
- JWT ailesi spesifikasyonlarındaki sorunlar JWT: The JSON Web Token standard is bad and everyone should avoid it yazısında daha derin ele alınır
İtirazlar
- “Google da JWT kullanıyor” itirazı, tarayıcıdaki kullanıcı oturumları için geçerli değildir
- Google, tarayıcı kullanıcı oturumları için JWT kullanmaz; normal çerez oturumları kullanır
- JWT, yalnızca bir sunucu veya host üzerindeki oturum açma oturumunu başka bir sunucu veya host üzerindeki oturuma aktarmak için bir Single Sign On taşıma aracı olarak kullanılır
- Bu kullanım biçimi, JWT’nin makul kullanım senaryoları kapsamına girer
- Google, daha güvenli bir JWT uygulaması oluşturup sürdürmek için gerekli güvenlik uzmanı kaynaklarına sahiptir
- Google’ın JWT’si fiilen başka yerlerdeki JWT’lerle aynı değildir
- “Durumsuz olmak daha iyidir” itirazı, güvenli kimlik doğrulama gereksinimleriyle uyuşmaz
- Çok büyük kaynaklar olmadan gerçekten durumsuz kimlik doğrulamayı güvenli şekilde işletmek mümkün değildir
- İlgili tartışma için Stateless is a lie bağlantısına bakılabilir
- “Oturumun nasıl kurulacağını bilmiyorum” sorunu, çoğu web sunucusu framework’ünün dokümantasyonu ve uygulamalarıyla çözülebilir
- Oturum teknolojisi özellikle yeni olmadığı için, oturumları açıklayan yazılarla sık karşılaşılmaz
- Oturum uygulamasının dokümantasyonu bile kurulum sürecini izlemek için yeterli olmalıdır
- Neredeyse tüm web sunucusu framework’lerinde oturum uygulaması bulunur; varsayılan olarak etkin olmasa bile genelde kolayca etkinleştirilebilir
- Express ve diğer Node.js framework’leri, yüksek modülerlikleri ve tek amaca odaklı yapıları nedeniyle bir ölçüde istisnadır
- Express’te
express-sessionmiddleware’i ve kullanılan depoya uygun store connector yeterlidir - Postgres, MySQL veya mümkünse SQLite ile birlikte
connect-session-knexkullanılması önerilir
Kısa süreli token’lar
- Herhangi bir kullanım için kısa ömürlü imzalı token’lara ihtiyaç varsa, güvenlik için tasarlanmış PASETO adlı daha iyi bir spesifikasyon vardır
- PASETO kullanılsa bile oturum amacıyla kullanılmamalıdır
Oturumlar nasıl çalışır
- Oturumların nasıl çalıştığını daha iyi öğrenmek için joepie91’in gist’i incelenebilir
2 yorum
JWT, token şifreleme ve DB sorgularını azaltma yöntemidir; çerez tabanlı kimlik doğrulamanın karşısında konumlanan bir kavram değildir. JWT’yi secure cookie içinde saklarsanız, ele geçirilme riski legacy cookie kimlik doğrulama yöntemiyle aynıdır.
JWT’yi expired yapmak için bir son kullanma listesi yönetmek, performans açısından avantaj sağlar. Yalnızca son kullanma bilgisini Redis’ten sorgulamakla tüm üyeler için DB sorgusu yapmak arasında maliyet farkı vardır.
On binlerce kez 100 bin üyelik satır üzerinde indeks tabanlı sorgu (legacy cookie yöntemi)
vs
On binlerce kez Redis’te 50 kayıtlık son kullanma listesi sorgusu (JWT anında expire etme yöntemi)
JWT’nin avantajları vardır. Sadece küçük ölçekli ortamlarda fark daha az hissedilir.
Hacker News görüşleri
Gerekli ipucu eksik: Konu tarayıcı tabanlı kullanıcı oturumları hakkında
Servisler arası iletişimde JWT’nin gayet iyi kullanılabildiği birçok durum var
Ek olarak, bağlantı verilen yazının bir kısmını okudum; örneğin https://paragonie.com/blog/2017/03/jwt-json-web-tokens-is-ba... gibi bir yazı var. JWT gerçekten bu kadar korkunç derecede güvensiz bir standartsa AWS STS’nin AssumeRoleWithWebIdentity özelliğini nasıl hackleyeceğini açıklayabilir ya da açıklamayıp Fortune 500 şirketlerinin prodüksiyon AWS hesaplarının her birinde kripto madencisi çalıştırabilirsin. JWT madem bu kadar güvensiz, başarırsan haber ver /alay
JWT’nin imza/şifreleme tarafı karmaşık ve yaygın JWT kütüphaneleri ancak sonradan büyük ölçüde akıllandı; eskiden durum böyle değildi.
"none"algoritmasını kabul eden çok sayıda kütüphane vardı [1], ayrıca açık anahtarı paylaşılan sır gibi kullanıp saldırganın token sahtelemesine izin veren örnekler de oldu [2]. Bağlantı verilen yazının eleştirdiği karmaşıklığın doğrudan sonucu bunlarJWT bazen kullanıcı oturumlarında istenen işlevi de sağlayamıyor. Bir yerde iptal listesi tutmadan geçersiz kılamazsın. Ama her istekte tanımlayıcıyı iptal listesiyle karşılaştıracaksan, bunun yerine opak bir oturum kimliği kullanıp her istekte onu sorgulayabilirsin. Elbette kısa ömürlü token kullanıp sürekli yenileyebilirsin, ama zaten durum tutan sıradan uygulamalarda bunu yapmak için güçlü bir neden yok
Öte yandan dağıtık sistemlerde veya makineler arası iletişimde imzalı tokenların faydalı olabildiğine tamamen katılıyorum. Bu iki durumu birbirine karıştırmamak gerekiyor
[1] https://nvd.nist.gov/vuln/detail/cve-2022-23540
[2] https://nvd.nist.gov/vuln/detail/CVE-2024-54150
Şimdi çeşitli dillerdeki ana kütüphaneler daha düzgün varsayılanlara sahip olduğu için, bugünlerde pratikte epey güvenli hale geldiğini düşünüyorum
“Doğru tutup kullanırsan güvenlidir” demek otomatik olarak iyi tasarım anlamına geliyorsa, aynı şey X.509 için de söylenebilir
Çoğu durumda daha iyi alternatifler var. Standart oturum tokenları veya API anahtarları büyük web sitelerinin çoğunda yaygın biçimde kullanılıyor ve çoğu kullanım senaryosuna neredeyse kusursuz uyuyor
Bu standartların hiçbir değeri yok demiyorum. En iyi yanları, ASN.1 kodlaması gibi şeyler olmadan bir şeyleri alışveriş etmeye yarayan temel bir standart sunmaları; ASN.1 tarafındaki araçlar ise çok kırılgan ve hataya açık görünüyor
Örneğin SAML’nin nasıl istismar edileceğini bilmiyorum ama tüm XML ayrıştırıcısını saldırı yüzeyine çevirdiği için korkunç bir standart olduğunu biliyorum. Güvenlik araştırmacısı değilim, XML ayrıştırıcısında zafiyet bulmayı da bilmiyorum; ama saldırı yüzeyinin büyük olmasının kötü olduğunu anlayabiliyorum
JWT’nin güvensiz olduğunu mu söylüyorsunuz; güvenilir RSA/açık anahtar tabanlı imza kullanıldığında bile mi? Paylaşılan sır olmasa da?
JWT’nin fazla uzun ömürlü olduğu iddiası da garip. JWT ömrünü sınırlayıp yetkilendirme kurumu için bir yenileme modeli kurabilirsin. Çerez tabanlı oturum kullansan bile sonuçta bir yerde bir şey depoluyorsun. JWT’yi sadece 5–15 dakika geçerli yapabilirsin; 15 dakika, Entra dahil birçok yetkilendirme sisteminin önbellek süresine benziyor. 5 dakikalık tokenlar bile yenileme sistemi varsa tarayıcıda gayet kullanılabilir
Son olarak, kimlik/doğrulamayı uygulama ve API servislerinden ayırmayı tercih ediyorum. Bağlamı dışsallaştırabiliyorsun ve her istekte JWT işleme yaklaşımı, ara sıra başarısız olabilen paylaşımlı önbellek/durum sistemlerinden daha kolay yönetiliyor. İmzalı tokenlar için imza bilinen bir otoriteye karşı doğrulanabiliyor
Bunun dışında imza kriptografik olarak geçerlidir. Tüm JWT’leri kısa ömürlü yapıp her seferinde doğrulayabilirsin
Bilgi olsun diye: OIDC tokenlarının hepsi JWT’dir
Oturum ile JWT iptal listesini karşılaştırınca, JWT iptal listesi lehine bir mantık da var. JWT’nin sınırlı bir son kullanma zamanı olduğundan, iptal listesini yalnızca süresi henüz dolmamış tokenlar için tutmak yeterlidir
Dolaşımdaki geçerli JWT’lere kıyasla iptal edilmiş JWT’ler muhtemelen küçük bir alt küme olacağından, her istekte çok küçük bir veri kümesini sorgulaman yeterli olur
Oturum kullanırsan geçerli oturum listesi iptal listesinden birkaç büyüklük mertebesi daha büyük olabilir; dolayısıyla durum saklamanın sorgu maliyeti ve depolama maliyeti daha yüksektir
Ayrıca yazı JWT’nin durumsuz olduğunu söylüyor ama çoğu zaman öyle değil. Genelde sadece JWT doğrulamazsın; her istekte eşleşen kimlik nesnesini, yani kullanıcı ayrıntılarını çekip kullanıcının hâlâ aktif olup olmadığını ve o işlemi yapma yetkisine sahip olup olmadığını da kontrol edersin. Kullanıcı bazlı iptal listeleri veya
minimum_issued_atgibi bir değer kullanarak JWT’dekiiatalanını doğrulayabilirsin. Bu sayede “tüm cihazlarda çıkış yap” deseni de mümkün olur; bu işlem kullanıcınınminimum_issued_atdeğerini$NOWyapınca önceki tüm tokenlar geçersiz hale gelir. Tek tek iptal listesi sorgusu gerekmezselecttir ve 0–1 satır döndürür. Çoğu durumda dert edilecek bir şey değilBu yazı, “neden” kısmının büyük bölümünü başka blog yazılarına linkleyerek açıklıyor; o blog yazıları da genel olarak “tekil JWT token’ları geçersiz kılamazsınız” diye şikâyet ediyor gibi görünüyor
Benim bir şey implemente ettiğim her seferde genel kural, bir yerlerde geçersiz kılınmış nonce’u kontrol etmekti; bu da o yazının ikinci iddiasını çözüyor
“JWT spesifikasyonunun kendisine güvenlik uzmanları güvenmiyor” iddiası, tek bir blog yazısından daha fazla dayanak gerektiriyor gibi duruyor. Ayrıca o yazı daha çok kötü implementasyonları suçluyor gibi; ama hangi standart olursa olsun kötü implementasyon sorunu peşinizi bırakmaz
Genel olarak, rastgele bir gist linkine tıklarken ne beklediğimi bilmiyorum
Bunun dışında tarayıcıda daha kısa ömürlü JWT’leri rahatlıkla kullanabilir ve agent’ın bunları kendi kendine yenilemesini sağlayabilirsiniz. Azure Entra veya başka birçok sağlayıcı kullanırsanız pratikte zaten böyle çalışır. JWT’yi nispeten kısa, yaklaşık 5–15 dakika tutabilir ve
jtiiptal durumunu da kontrol edebilirsinizJWT, yetki veren otoriteyi uygulama/API sisteminden ayırıp yeniden kullanmak için çok kullanışlıdır. Saldırı yüzeyini taşırsınız ama güvenilir bir şekilde taşırsınız. Dünya genelinde SSH dahil birçok yerde açık anahtar yaklaşımı kullanılıyor. Paylaşılan sırlar veya uzun ömürlü token’lar kullanmazdım ama doğrulanmış ve bilinen kaynaklardan gelen kısa ömürlü, açık anahtarla imzalanmış token’lar çoğunlukla sorun değildir
Hatta pratikte asıl sorun çoğu zaman API key’ler oluyor. Az önce bir tane implemente etmem gerekti; benim durumda API key’i de Bearer token gibi görünecek şekilde yaptım: kısa bir
sak.öneki, ardından kimlik kısmı (base64url UUID baytları), sonra da gizli değer (base64url baytları). Veritabanında UUID ile gizli değerden üretilmiş passphrase düzeyinde bir salt+hash saklıyorum. Dolayısıyla üretilen API key gizli kabul edilmeli ve veritabanında sadece tek yönlü olarak tutuluyor; böylece DB ihlali doğrudan kimlik doğrulama ihlaline dönüşmüyorBuna rağmen, düzgün implemente edilmiş bir JWT çözümünde sorun çıkmasından çok API key sızıntısı yaşanması daha olası
Bu yazıya tesadüfen denk geldim ve geçmişte bu konuda çok çalıştığım için yeniden gündem olması ilginç geldi. Sonra tıklayıp bakınca yazarın benim bazı materyallerime link verdiğini gördüm. Çok eski anılar canlandı
Her neyse, benden çok daha zeki insanlar bu konuyu yıllardır geniş biçimde ele aldı ama 2026’da bile JWT’nin web kimlik doğrulaması için yanlış araç olduğunu düşünüyorum. Servisler arası kullanım için uygun ama seçme şansınız varsa doğrudan PASETO kullanmak daha iyi. Birçok sorunu çözüyor
https://www.paseto.io/
Şu anda web sitesine bildirim push’u için RabbitMQ ekliyorum. İstemcinin nerede ne okuyabileceğini kontrol etmek için JWT kimlik doğrulaması kullanıyorum; kısa ömür ve düzenli token yenilemesi de var
Bu kurulumun kolaylığına yaklaşan başka bir yapı pek bilmiyorum. Geçerli oturuma JWT token veren tek bir endpoint eklemek yetiyor ve kullanıcı bazlı yetkilendirme de mümkün oluyor
JWT kullanmamanız gerektiğini anlatan bağlantılı yazılardan biri, iyi niyetle bakılsa bile tuhaf
https://paragonie.com/blog/2017/03/jwt-json-web-tokens-is-ba...
Özetle “bazı kütüphanelerde bug vardı” diyor, ardından da libsodium getirip işi kendiniz yapmanızı öneriyor. Bu, ciddiye alınması zor, saçma bir tavsiye. Her yazılımda bug olur. Heartbleed sırasında bütün internet ayağa kalkmıştı ama biz hâlâ TLS ve OpenSSL kullanıyoruz
“JWT spesifikasyonu özellikle çok kısa ömürlü token’lar, yani kabaca 5 dakika ve altı için tasarlanmıştır” ifadesini ilk kez duyuyorum ve bunu destekleyen bir kaynak da bulamıyorum. RFC 7519’da böyle bir iddia yok
Genelde JWT’yi bir kimlik doğrulama önbelleği gibi kullanıyorum. Kimlik doğrulama servisinden bir auth token alıyorsunuz ve bu token başka servislere yetki veriyor
Bunun çeşitli avantajları var ama ana fikir şu: alt servislerin ne kimlik doğrulama veritabanıyla etkileşmesi gerekiyor ne de token üretme yetkisine sahip olmaları gerekiyor. Burada HMAC değil RS256 kullanıldığı varsayımı var. Dolayısıyla alt servis ele geçirilse bile, bu, kimlik doğrulama veritabanına erişebilen bir servisin ele geçirilmesi kadar yıkıcı olmuyor
Token içinde hassas veri varsa JWE kullanmanız gerekir ama bu da her kullanımda özel anahtara sahip bir iç servisten token’ı çözmesini istemeniz gerektiği için pek iyi sayılmaz
Benim sık kullandığım yapı
{"id": (uuid), "scopes": ["scope:read/write"]}şeklindeSPA için de oldukça iyi. Çünkü statik site sunucusu, resource’ları servis etmeden önce JWE’yi açık anahtarla doğrulayabiliyor. Benim yöntemimde statik siteyi
/(scope)/pathbiçiminde derliyorum ve statik servis, erişimi olmayan sayfaları baştan hiç sunmuyor. Özellikle yönetim paneli gibi, backend’in sahip olduğu yetenekleri veya saldırılabilir iç servis yollarını kullanıcıya göstermek istemediğiniz durumlarda çok kullanışlı“Backend erişimi” için JWT ömrü yaklaşık 5 dakika ve
/megibi şeyleri,/refreshlocalStorage önbelleğinin atılmasını açıkça söylemediği sürece localStorage’da cache’liyorum. SPA uygulamasının request handler’ı “yenileme gerekli” durumunu algılayıp token’ı yeniliyorBunun sorumluluğunun büyük kısmının node/next ve Python kütüphanelerinde olduğunu düşünüyorum. Backend’i güçlü tipli bir dille yazıyor, frontend’i ise her zaman önceden derlenmiş statik sayfalar olarak üretiyorum. Şu anki frontend kurulumunda VITE kullanıyorum; landing kısmı önceden render edilmiş sayfalardan, uygulama kısmı ise normal bir SPA’dan oluşuyor
Bütün bunları hesaba katsam bile bu gist’in tamamına güçlü biçimde katılmıyorum. JWT’yi istediğiniz kadar güvenli hâle getirebilirsiniz
JWT gayet uygun; başlık ise biraz sansasyonel duruyor
Bunun yerine konuşmaya daha değer konular var: şifrelenmiş değerlerin (simetrik veya asimetrik), rastgele ama gizli değerlerin, imzalı değerlerin (okunabilir ama değiştirilemez değerler) ne zaman kullanılması gerektiği; bunların nereye konulacağı (bellek, localStorage, cookie); değerlerin sonsuza kadar yaşamamasını nasıl sağlayacağınız ve doğal sona erme zamanından önce iptal edilmelerinin gerekip gerekmediği gibi şeyler