Misaliperver: Nedir Bu JWT? (Json Web Token 1. Kısım)

Kısa bir öykü ile başlayalım..

İster SSR ile geliştirilmiş postback bir uygulamanız olsun,  single page'te çalışabilir tabiki, isterseniz FSR ile çalışan single page bir uygulamanız olsun, postback'te çalışabilir tabiki; ister multitenancy bir uygulamanız olsun, isterseniz 3rd party açmak üzere işletmenize ait bir restful servis geliştirin, soap'ta olabilir tabiki..

Of! Kavramlar çok havada kalıyor. Bazen yapacağınız uygulamayı ifade etmek için kullandığınız terimler toplantı sırasında paydaşların tecrübesine bağlı olarak farklı anlaşılabiliyor. 
Tüm bunları bir tarafa bırakalım, security(güvenlik) ne olacak, bu güvenliği aramızda nasıl sağlayacağız.
  1. Sen benim sana verdiğim kullanıcı adı ve şifre ile sisteme authenticate ol
  2. Yok yok benim uygulamam domain bazlı bir multitenancy sağlamıyor, sen sana verdiğim kullanıcı adı, şifre ve sana özel üreteceğim müşteri belirteci ile auth türünü belirterek Authenticate ol
  1. Ben sana header'ın cookie'sine bir token ileteyim sen benim diğer apilerime istek at
  2. Yok yada ben sana bir json tipinde veri döneyim. İçinde success true yazıyorsa, token'ı kullanarak diğer apilerime istek at
  3. Yok yada header'da 200 görüyorsan sana döndüğüm xml içerisindeki token bilgisiyle istek at

Seneryolar çok fazla yazılım dünyasında. Her zaman derim "en doğrusu" diye bir kavram maalesef yok. Uygulamanın ihtiyacına bağlı olarak kullanıyorsun sadece.

Bu serinin ilk yazısı tamamen teorik, bir sonraki yazıda Authentication ile müşteriyi login apimizi kullanarak sisteme giriş yapabilmesi için doğrulama işlemi yapıp, jwt token üreteceğiz; sonrasında müşteri diğer apilerimize istek atarken Authorization ile oturum süreçlerimizi yöneteceğiz.


Peki JWT'ye gelelim. [Wikipedia bilgisi]


JSON Web Token, tarafların birbirleri arasındaki veri alışverişini ve bunun doğrulamasını sağlayan JSON tabanlı RFC 7519'de tanımlanmış açık bir standarttır. 

Jwt.io sitesinde denemeye başlayabilirsiniz.

Yapısı

Header.Payload.Signature

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkZhdGloIENhbiBLZXNlciIsImlhdCI6MTUxNjIzOTAyMn0.sDlPl3gOeJWvoGE58pHWfY4RuAwp-RNW6bjiH5I-jP4

Araları . (nokta) eklenerek ayrılan üç kısımdan oluşan, encrypted oluşturulan bir veridir.

İlk 2 kısım base64 ile kodlandığı için herkes tarafından görülebilir. Payload kısmı için deneyelim;

 


İlk kısım Header(Başlık), token'ın özelliklerini belirtir.

{
  "alg": "HS256",    // Jwt'in signature'ı için kullanılan algoritma
  "typ": "JWT"   // Token tipi
}

İkinci kısım Payload(Veri), token'ın içine yükleyeceğimiz veriyi ve bir takım jwt'ye has uniqeue özellikleri belirtiyor.

{
  "sub": "1234567890",
  "name": "Fatih Can Keser",
  "iat": 1516239022
}

 Etiket Açıklama
 iss JWT'yi oluşturan/veren kuruluşun adı.
 sub JWT'nin alıcısını belirten eşsiz değer.
 aud JWT'yi yürütecek tarafı belirten değer. Eğer değer yürütmeciyle eşleşmiyorsa JWT reddedilir. 
 exp JWT'nin hangi süreye kadar geçerli olduğunu belirten değer. Sayısal formatta olmalıdır.
 nbf JWT'nin hangi süreden önce geçerli olamayacağını belirtir. Sayısal formatta olmalıdır.
 iat Anahtarın oluşturulma zamanını içeren bilgiyi taşır. Sayısal formatta olmalıdır.
 jti Büyük/küçük harfe duyarlı, JWT'yi tanımlayan eşsiz anahtar kodu.

Üçüncü kısım Signature(İzma), token'nı oluşturuken, header + payload alanların secret key ile şifrelendiği benzersiz doğrulama için kullanılacak kısım.

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  your-256-bit-secret
)

Güvenlik nasıl sağlanıyor?

Ne demiştik yukarıda, signature HMACSHA256( base64(header) + base64(payload) + secretkey ) ile şifrenelerek iletiliyor.

Herhangi bir apinize JWT geldiğinde, nodejs kütüphanelerinde jsonwebtoken.verify(gelenjwt) diyerek doğrulama işlemini yapıyorsunuz. 

Peki mantığı nasıl çalışıyor?

HMACSHA256 algoritması için düşünecek olursak, HMACSHA256( base64(header) + base64(payload) + secretkey )  aynı enycripte'leme işlemini yapıp, gelen veri ile eşit olup olmadığına bakıyor. Eşitse doğrulanmış oluyor. Değilse payload'ı okuyabiliyor ama doğrulanamıyor.

HMACSHA256 da signature karakter uzunluğu 43 oluyor.
HMACSHA384 da signature karakter uzunluğu 64 oluyor.

RSA şifrelemelerinde karakter sayısı gittikçe artıyor.

HMACSHA256 da kısa bir secret_key yerine, 32 karakter veya daha uzun bir şifre belirlemenizi öneririm. Böylece Brute Force'tan daha kolay yırtabilirsiniz :)

If you use a long confusing secret_key while you enycripte, you will be off the hook with Brutforce.

Böylece Avantajlarımız...

JWT Tokenımız stateless çalışıyor. Session doğrulaması yapmak için veritabanına istek atıp kontrol etmenize gerek kalmıyor. Hem veritabanı query'sinin bağımlılıklarından(istek için açılan tcp bağlantısı, cpu, ram, veri tabanı sunucundaki cpu, ram, disk kullanımından, ...) kurtulmuş oluyoruz

Hem de veritabanı aradan çıktığı için, sorugu için harcanan süre azalıyor. Haliyle daha performans'lı ve daha hızlı bir authorization yöntemi kullanmış oluyoruz.

Portable çalıştığı için, birden fazla sunucuda(back-end) veri tabanı bağlantısız doğrulama işlemi yaparak gönül rahatlığı ile kullanabiliyorsun.

Oha adamlar devrim mi yaratmış, olumsuz bir şeyi yok mu?

Var tabiki. Token'ın bir şekilde kimlik hırsızının eline geçti diyelim. Oturum için kullandığınız token arkniyetlilerin elinde.

İlk akla gelen şifreyi değiştirmek olur. Okey! Gittin ve şifreni değiştirdin.

Dostum JWT token'ı veritabanına sormuyorsun, stateless olarak taşınıyor. İşler hiçte gönül rahatlığı ile kullanabileceğin şekilde ilerlemiyor. O jwt'nin süresi dolana kadar, ilgili kimlik hırsızı istediği işlemi yapmaya devam edebilir.

Bunun bir çözümü var mı ki?

Yerine göre kullanmakta fayda var diye düşünüyorum. Öyle herşeye JWT Token Auth eklenmemeli 
-benceli.

Kısa vadeli çözüm için payload'a username veya user uniqeue id eklenerek, veritabanında oluşturacağımız last_password_change_time parametresi kullanılarak, yapılan her istekte veri tabanına sorgu atarak, bu tarihten önceki tokenların hizmet almasını önleyebilirsiniz.

Veya veritabanına her kullanıcı için (password değiştiğinde yeniden random atanacak) bir secret_key değeri eklersiniz. Kullanıcı eğer password'u değiştirirse veya tüm cihazlarda oturumu sonlandır derse, kullanıcı için secret_key değiştirilerek, JWT verify işleminde farklı bir secret_key atandığı için önceden üretilmiş tokenlar geçersiz kalacaktır.

Başka yöntemler de geliştirebilirsiniz tabiki..

Nerde kaldı avantajı?

Veritabanı kullanımı bir noktada şart oluyor maalesef. Hele ki güvendiğiniz partnerler yerine B2C'ye hizmet veriyorsanız..


Bir sonraki yazıda tezgaha geçip, biraz işin kodlama kısmına gireceğiz. Görüşmek üzere.

Yorumlar