Öncellikle, Json Web Token, sık kullanılan adı ile JWT adını duyamayanlar için kısa bir özet geçerek başlayalım, fakat yazının asıl amacı “JWT nedir?”i anlatmaktan ziyade, “JWT ne değildir?” i anlatmak.
JWT access token’ler (erişim jetonları) oluşturmayı sağlayan bir standart. Bu token genellikle sunucu tarafında oluşturularak (şart değil) istemciye gönderilir. Taraflar sunucunun özel anahtarı ile şifrelenen bu token’i doğrulayarak içindeki verinin gerçekten sunucu tarafından oluşturulduğuna emin olabilirler.
JWT imzalanırken sunucunun özel anahtarı ile imzalandığı için bu anahtara sahip olmayan kimse bu bilgiyi değiştiremez. Yani istemcinin gönderdiği JWT token sunucu tarafından doğrulanıyorsa içerisindeki “kullanıciadi”=”evrenbal” bilgiside güvenilirdir. JWT içerisinde “dogumyili”=”1980” bilgisi varsa veritabanına bağlanıp “evrenbal” ın doğum tarihini getirmemiz gerekmez, bu işlemi daha önce yapıp o token içerisine “1980” yazanın sunucumuz olduğundan emin olabiliriz.
JWT ne değildir?
Sanılanın aksine eğer bilinçli uygulanmazsa JWT güvenilir değildir. JWT kritik durumlarda kimlik doğrulama için kullanılamaz.
Peki ya “evrenbal” şifresinin çalındığını veya internet cafede kullandığı bilgisayardan çıkış yapmadığını farkederse? Muhtemelen hemen sayfaya girip şifresini değiştirir ve sorunu çözdüğünü düşünür. Fakat küçük bir sorun var, siz token’i kontrol ederken şifre kontrolü yapmıyorsunuz! Şimdi ne olacak, tokenden “evrenbal” kullanıcı adı gelmeye devam edeceğine göre kullanıcı adını mı değiştireceğiz?
JWT’nin geçerlilik süresini kısa tutup (Örneğin bir kaç saat), daha uzun süreli (Örneğin bir kaç ay) bir “refresh token” oluşturmak güzel bir seçenek olabilir.
Böylelikle bir “access token”in birkaç saat sonra etkisiz hale gelmesini sağlarken, 1 gün aradan sonra tekrar sitenize girmek isteyen kullanıcıya da tekrar giriş yaptırmadan yeni bir access token’i otomatik olarak oluşturabilirsiniz. Bu durumda ise şöyle bir handikap var, uzun süreli refresh token’i doğrulayabilmek için veritabanında saklamanız gerekiyor.
Gönderilen refresh token hala geçerli ve veritabanındaki ile uyumlu ise kullanıcıya tekrar sormadan access token oluşturabilirsiniz. Eğer kullanıcı şifresini değiştirdiyse veritabanındaki refresh token’i sıfırlayarak daha önce oluşturduğunuz tokenlerin geçersiz olmasını da sağlamış oldunuz. Hatta kullanıcı şifre değiştirirken bunu ona sorabiliriz bile.
Bulduğumuz çözüm gerçekten harika mı acaba? Hayır malesef değil, çünkü access token geçerlilik süresi boyunca, o birkaç saat içerisinde hesabı kullanan kişinin yetkisiz olduğunu anlama şansımız yok. Ne zamanki access token süresi dolar ve yenilenmesi gerekir, sunucumuz duruma ancak o zaman uyanacaktır.
Çözüm olarak birkaç saat değil, birkaç dakikalık acess token’ler kullanırım diyebilirsiniz. Bu durumda da hem hala birkaç dakika gecikme yaşayacak, hem de aslında sürekli veritabanı sorgusu yaparak JWT’yi düşündüğünüz şekilde kullanmıyor olacaksınız.
O zaman JWT’yi nasıl kullanacağım?
Yukarıdaki örnek için daha güvenli çözümler düşünmeye çalışalım.
Her iki senaryoda da; kullanıcımız hesap şifresini çaldırmış veya internet cafe’de çıkış yapmadan hesabını açık bırakıp gitmiş olsun.
Kısa çözüm:
JWT token’imizde bir değişiklik yapmadan kullanalım. Fakat her istekte “evrenbal” ın son şifre değiştirme tarihini kontrol edelim (veri tabanından, hafızadan, redis vb. bir çözümle, orası istediğiniz performansa kalmış). Eğer JWT oluşturulduktan sonra şifre değiştirildiyse isteği reddedelim.
Sonuç: Hesap çalındı veya Internet cafede açık unutuldu, şifre değiştirildiği anda sorun ortadan kalkar, fakat “evrenbal” iş yerindeki bilgisayarından, mobil telefonundan, evdeki bilgisayardan hepsinden tekrar giriş yapmak zorunda kalır. JWT token içerisinde sakladığımız başka bilgiler de varsa onları da otomatik reddetmiş oluruz.
Uzun ama daha etkili çözüm:
Kullanıcı her login olduğunda giriş yaptığı cihaz için benzersiz bir ID oluşturalım. Örneğin bu ID “ABCDE” olsun.
Sunucu session (oturum) bilgilerinde, veri tabanında veya performans açısından hafıza veya redis gibi bir çözümde “ABCDE” bilgisini saklayalım. Benzersiz ID ile beraber cihaza ilişkin bazı ek bilgileri de isteğe bağlı saklayabilirsiniz. (Örneğin şu IP’li, şu işletim sistemli, şu browser kullanan cihaz gibi)
İstemciye göndereceğimiz JWT içerisinde “ABCDE” bilgisini gönderelim. Ve token bize her gönderildiğinde “ABCDE” cihazını sunucu tarafında kontrol edelim.
Sonuç: Diyelim ki kullanıcı şifresini değiştirdi; daha önce giriş yaptığı cihazları sunucudan sildiğimizde, daha önce servis edilen bütün JWT’ler geçersiz hale gelecektir. Veya buna gerek kalmadan kullanıcı sayfasında “oturum açmış cihazlar” başlıklı bir bölüm yapıp, tanımadığı cihazların oturumunu kapatarak bu sorunu çözmesini sağlayabiliriz. (Örneğin Internet cafede açık bıraktıysa son giriş yaptığı cihazı silmek gibi)
Bütün bunlara gerek var mıydı?
Peki JWT ile bu kadar uğraştıktan sonra JWT içerisinde sadece cihaz bilgisi olan “ABCDE” yi saklayacaksak acaba gerçketen JWT’ye gerek var mıydı? Aslında hayır, bu bilgiyi bir çerez (cookie) içerisinde, çok daha düşük boyutla, ekstra bir JWT kütüphanesine gerek duymadan, bütünleşik çözümlerle halledebilirdik.
Evet JWT çok güzel bir teknoloji ve özellikle ilk öğrendiğinizde her şeyi JWT kullanarak yapmak istiyorsunuz, ama bir projede JWT entegrasyonu yapmadan önce, kısa bir duraksayıp “gerçekten gerek var mı, aynı güvenilirlikte ve daha basit halledilebilir mi” diye kendimize sormamız gerekiyor.