JWT(JSON Web Token) & PASETO(Platform-Agnostic Security Tokens) Kapsamlı Karşılaştırmasını Yapalım
Merhaba,
Günümüzde modern web uygulamalarının güvenlik yapılanmaları token-based authentication’a dayanmakta ve bu, geleneksel session-based authentication’a nazaran daha gelişmiş bir yapılanma sağlamaktadır. Bizler bu güne kadar çeşitli token yaklaşımları arasından basitliği, uygulama kolaylığı ve en çokta popülaritesi nedeniyle yaygın olarak JWT(JSON Web Token)’yi tercih etmiş bulunmaktayız. Amma velakin bu ön kabuller doğrultusunda JWT yanında olası güvenlik açıkları gibi endişeler getirmekte ve bu durum biz yazılım geliştiricileri daha sağlam alternatif arayışlarına sürüklemektedir. Yaptığımız gözlemlerden ve kimi tecrübeli yazılımcıların edindiği deneyimlerden yola çıkarak, PASETO(Platform-Agnostic Security Tokens)’nun, JWT’nin eksikliklerini doğrudan ele alan daha iyi bir çözüm olarak ortaya çıktığı ve daha güvenli bir temel üzerinden davranış sergilediği kanaatine varmış bulunuyoruz. Haliyle bizler bu içeriğimizde PASETO ile JWT arasındaki avantaj ve dezavantajları masaya yatıracak ve böylece güçlü ve zayıf yönlerini analiz ederek projelerde hangisinin tercih edilmesi gerektiğine dair bilinçli kararlar verilebilmesi için doğru bilgilerle zihinlerimizi donatacağız ve biryandan da yapılandırma süreçlerini ele alarak kapsamlı bir karşılaştırmada bulunacağız.
İlk olarak token-based authentication’ın çalışma mantığını ele alalım. Ardından bu mantık doğrultusunda JWT ve PASETO teknolojilerinin davranışlarını detaylandıralım. Böylece iki yapı arasındaki farkı daha rahat ortaya koyabilir ve istişarelerimizi yapabiliriz.
Token-Based Authentication Nasıl Çalışmaktadır?
Modern uygulamalarda kullanıcıların API’lere erişimini yönetmek için güvenli ve verimli bir yol olarak tercih edilen token-based authentication yaklaşımı, kullanıcının bir kere kimliği doğrulandıktan sonra bir token alması ve bu token’ı kullanarak erişim sağlaması davranışına dayanmaktadır.Token-based authentication’ın akış mantığı adım adım şu şekildedir;
- Kullanıcı Giriş Yapar
Kullanıcı, kullanıcı adı ve şifre gibi kimlik bilgilerini kullanarak sunucuya giriş talebinde bulunur. - Sunucu Kimliği Doğrular ve Token’ı Üretir
Sunucu, kullanıcının kimlik bilgilerini kontrol eder. Eğer kimlik doğrulama başarılıysa, bu kullanıcıya özel olarak tasarlanmış ve kullanıcının kimliğinin ve erişim haklarının güvenli bir temsili olarak işlev görecek olan, içerisinde kullanıcı bilgilerini ve izinlerini içeren benzersiz, dijital olarak imzalanmış bir token oluşturulur. İşte bu token genellikle JWT formatında olmaktadır. - Sunucu Token’ı Kullanıcıya Gönderir
Doğrulamanın başarılı olması durumunda, sunucu bu token’ı kullanıcıya gönderir. - Kullanıcı Token’ı Depolar
Kullanıcı sunucudan edindiği token’ı sonradan kullanabilmek için depolayacaktır. Bu işlem genellikle browser üzerinden local storage yahut session storage bölümlerinde gerçekleştirilir. Eğer ki client bir web uygulaması değil de mobil yahut masaüstü bir yazılımsa genellikle hafızada depolanarak saklama işlemi gerçekleştirilir. - Her İstekte Token Gönderilir
Kullanıcı, korumalı bir kaynağa yahut API’ye erişmek istediğinde bu token’ı HTTP request header’ına Authorization key’i eşliğinde ekler ve sunucuya gönderir. - Sunucu Token’ı Doğrular
Sunucu gelen isteklerdeki token değerlerini doğrular. Eğer token geçerliyse sunucu isteği işler ve kullanıcının istediği kaynağa erişim izni verir. Yok eğer geçersizse işte o taktirde erişim engellenir. - Token’ın Süresi Dolabilir
Token genellikle belirli bir süre için geçerli oluşturulmaktadır. Süresi dolmuş bir token ile yapılan istek neticesinde doğrulama sağlanamayacağı için kaynağa erişim gerçekleştirilemez.
Şimdi hususi olarak JWT ile PASETO’yu incelemeye geçebiliriz.
JWT Nedir? Nasıl Çalışmaktadır?
JWT(JSON Web Token), iki sistem arasında güvenli bilgi alışverişi için kullanılan, bilgilerin JSON formatında kodlanıp dijital olarak imzalandığı bir standarttır. Genellikle kimlik doğrulama, yetkilendirme ve özel kaynaklara erişim izni süreçlerinde sıklıkla kullanılmaktadır. Herhangi bir merkezi otoriteye bağımlı olmaması, distributed sistemlerde kullanılmasını kolaylaştırmaktadır. Yapısal olarak içeriğimizin devamında daha da detaylandıracağımız Header, Payload ve Signature olmak üzere üç ana bileşenden oluşmaktadır. Şimdi gelin JWT’yi bu yapıtaşları üzerinden inceleyelim;
JWT’nin Güvenlik Açısından Endişeleri Nelerdir?
JWT, birçok avantaj sunarken doğru şekilde uygulanmadığı taktirde ortaya çıkabilecek potansiyel tuzaklar ve güvenlik endişeleri söz konusu olabilmektedir. Misal olarak, JWT imzalanırken algoritma seçiminde esneklik sağlamaktadır ve bu esneklik ‘hiçbiri’ seçeneği de dahil olabilecek derecede geniş sınırlara kadar dayanabilmektedir. Eğer ki geliştiriciler, uygulamalarını yanlışlıkla imzasız JWT’leri kabul edecek şekilde yapılandırırlarsa işte bu durumda ciddi bir güvenlik zafiyeti söz konusu olabilmektedir.
Bunun dışında JWT’ler stateless olduğu için oturum bilgisini tutmak için sunucu üzerinde depolanmazlar. Çünkü bilindiği üzere JWT’lerin geçerlilik süresi vardır. Hal böyleyken bir kullanıcı oturumunu kapattığı taktirde JWT geçersiz kılınmış olmayacak ve süresi bitene kadar hala kullanılabilir olacaktır. İşte bu durumda JWT’leri geçersiz kılmak için geliştiriciler açısından ekstradan uğraş gerecektir.
PASETO Nedir? Nasıl Çalışmaktadır?
PASETO(Platform-Agnostic Security Tokens), yukarıdaki satırlarda JWT ile ilgili bahsettiğimiz endişelerin aksine daha basit ve güvenilir bir alternatiftir diyebiliriz. 2018 yılında Paragon Initiative Enterprises tarafından tanıtılmıştır. Taşınabilir ve platformdan bağımsız bir şekilde kullanılmak üzere tasarlanmış bir token yapısı sunmaktadır.
PASETO davranışsal olarak iki modda çalışmaktadır;
| Yerleştirilmiş (Embedded) Mod | Dışa Aktarılabilir (Exportable) Mod |
|---|---|
| Simetrik key’lerle çalışan token oluşturur. | Asimetrik key’lerle çalışan token oluşturur. |
Ayrıca PASETO, Local Tokens ve Public Tokens olmak üzere iki farklı token amacına sahip yaklaşım sergileyebilmektedir;
| Local Tokens | Puclic Tokens |
|---|---|
| Bu token’lar, state bilgisi olduğundan dolayı sunucu tarafından güvenli bir şekilde depolanabilmektedirler. | Bu token’lar ise tıpkı JWT’de olduğu gibi stateless uygulamalar ve public-key cryptography içeren durumlar için tasarlanmıştır. Bundan dolayı sunucu tarafı depolamaya ihtiyaç duyulmaksızın güvenli bir şekilde iletilerek, doğrulanabilmektedirler. |
PASETO, hem local hem de puclic token’lar da aşağıdaki gibi üç bölümden oluşan bir yapıya sahiptir;
| Header | Payload | Footer |
|---|---|---|
| Bu bölüm, adından da anlaşılacağı üzere ilgili token’ın kullanılan şifreleme algoritmasıyla birlikte PASETO sürümünü ve amacını(local&public) tanımlar. | Token’da ki verileri içerir. | Bu kısım ek meta verilerini taşımak için kullanılabilir. Token’ın doğrulama sürecine dahil edilmediği özellikle bilinmelidir. |
PASETO tüm bu özelliklerinin yanında token’ların şifrelenmesi ve imzalanması için güvenli kriptografik algoritmalardan istifade etmekte ve böylece token’ların gizliliğini koruyarak, bütünlüğünü sağlamaktadır.
PASETO’nun JWT’ye nazaran güvencesi, varsayılan olarak güvenli olacak şekilde tasarlanmış olmasıdır. Hatırlarsanız eğer JWT’de ise güvenlik tamamen developer tarafından doğru yapılandırmaya bağlıdır. Hangi token yapısını kullanırsanız kullanın PASETO’da güvenlik uzmanları tarafından seçilmiş ve test edilmiş belirli algoritmalar sabitlenmiştir ve aralarından birinin kullanılması elzemdir. Böylece JWT’de ki algoritma belirtmeksizin kullanım durumu gibi bir zafiyet kesinlikle ortadan kaldırılmakta ve biryandan da geliştiriciler açısından yanlış yahut güvensiz seçeneklerin seçilme ihtimali de törpülenerek güvenilirlik durumu daha idealize hale getirilmiş olmaktadır.
Tabi burada token amacına göre kullanılan algoritma yapılarında da bir genelleme yapılabilmektedir. Şöyle ki; local token’lar da genellikle tek bir secret key ile hem şifreleme(encryption) hem de şifre çözme(decryption) süreci seyrettiği için symmetric-key cryptography kullanılmaktadır. Bu da bu token’ları sunucu tarafı oturum yönetimi için uygun hale getirecektir. Benzer mantıkla public token’lar da ise şifreleme için bir public key ve şifre çözme için de private key içeren public-key cryptography kullanılmaktadır. Bu da private key’i paylaşmaksızın güvenli iletişim ve doğrulama yapılmasını sağlamaktadır.
Anlayacağınız,
JWT ve PASETO, her ikisi de güvenli token oluşturmak için kullanılan standartlardır. Ancak PASETO, güvenlik ve basitlik açısından daha avantajlıdır. Güvenlikle ilgili avantajı yukarıdaki satırlarda konuşmuştuk. Basitlik açısından değerlendirmede bulunursak eğer JWT’de çok farklı algoritmaların kullanılabilir olması developer kaynaklı hatalara sebebiyet verebilecekken, PASETO’da seçilmiş olan algoritmalar bu hata ihtimallerini törpülemektedir. Bu da basitlik ilkesi açısından bir avantajdır şeklinde yorumlanabilir. Bunların yanında, mevcut ekosistem ve destek açısından JWT daha yaygın kullanılsada hangi standardın tercih edilmesi gerektiği projesine ve güvenlik gereksinimlerine bağlı olarak değişkenlik gösterir.

JWT İle Token Based Authentication
JWT ile token-based authentication’a dair önceki yazılarımızda ya da youtube eğitimlerimizde onlarca kez bilgi paylaşmış bulunmaktayız. Bu içeriğimizi şişirmemek adına bu kaynakları referans etmekle yetineceğim;
- Asp.NET Core 3.1 ile Token Bazlı Kimlik Doğrulaması ve Refresh Token Kullanımı(JWT)
- https://www.youtube.com/watch?v=FskhAwnZcO0&t=2s
PASETO İle Token Based Authentication
PASETO ile token-based authentication için öncelikle ilgili uygulamaya Paseto.Core kütüphanesinin yüklenmesi gerekmektedir.
Ardından aşağıdaki adımlar eşliğinde PASETO uygulama genelinde yapılandırılabilir.
- Adım 1 GenerateToken – DecodeToken – ValidateToken Metotlarının Oluşturulması
Uygulamada PASETO token oluşturabilmek, oluşturulan bu token’ı çözebilmek ve doğrulayabilmek için aşağıdaki gibi bir handler sınıfı oluşturulabilir;public class PasetoHandler(IConfiguration configuration) { public object GenerateLocalToken() { var token = new PasetoBuilder() .Use(ProtocolVersion.V1, Purpose.Local) .WithKey(Encoding.UTF8.GetBytes(configuration["Paseto:Key"]), Encryption.SymmetricKey) .AddClaim("name", "gncy") .AddClaim("role", "mod") .AddClaim("email", "gyildizmail@gmail.com") .AddFooter("tel : 05077519999") .AddFooter("tel2 : 12345678901") .Issuer("www.gencayyildiz.com") .Subject(Guid.NewGuid().ToString()) .Audience("www.myapi.com") .NotBefore(DateTime.UtcNow.AddMinutes(5)) .IssuedAt(DateTime.UtcNow) .Expiration(DateTime.UtcNow.AddHours(1)) .TokenIdentifier("123456ABCD") .Encode(); return new { Token = token }; } public object GeneratePublicToken() { using var rsa = new RSACryptoServiceProvider(2048); var privateKey = rsa.ExportRSAPrivateKey(); var publicKey = rsa.ExportRSAPublicKey(); var token = new PasetoBuilder() .Use(ProtocolVersion.V1, Purpose.Public) .WithKey(privateKey, Encryption.AsymmetricSecretKey) .AddClaim("name", "gncy") .AddClaim("role", "mod") .AddClaim("email", "gyildizmail@gmail.com") .AddFooter("tel : 05077519999") .AddFooter("tel2 : 12345678901") .Issuer("www.gencayyildiz.com") .Subject(Guid.NewGuid().ToString()) .Audience("www.myapi.com") .NotBefore(DateTime.UtcNow.AddMinutes(5)) .IssuedAt(DateTime.UtcNow) .Expiration(DateTime.UtcNow.AddHours(1)) .TokenIdentifier("123456ABCD") .Encode(); return new { Token = token, PublicKey = Convert.ToBase64String(publicKey) }; } public PasetoToken DecodeLocalToken(string token) { var pasetoTokenValidationResult = new PasetoBuilder() .Use(ProtocolVersion.V1, Purpose.Local) .WithKey(Encoding.UTF8.GetBytes(configuration["Paseto:Key"]), Encryption.SymmetricKey) .Decode(token); var payload = pasetoTokenValidationResult.Paseto; return payload; } public PasetoToken DecodePublicToken(string token, string publicKey) { var pasetoTokenValidationResult = new PasetoBuilder() .Use(ProtocolVersion.V1, Purpose.Public) .WithKey(Convert.FromBase64String(publicKey), Encryption.AsymmetricPublicKey) .Decode(token); var payload = pasetoTokenValidationResult.Paseto; return payload; } public bool ValidateLocalToken(string token, out PasetoToken pasetoToken) { var pasetoTokenValidationResult = new PasetoBuilder() .Use(ProtocolVersion.V1, Purpose.Local) .WithKey(Encoding.UTF8.GetBytes(configuration["Paseto:Key"]), Encryption.SymmetricKey) .Decode(token); pasetoToken = pasetoTokenValidationResult.Paseto; return pasetoTokenValidationResult.IsValid; } public bool ValidatePublicToken(string token, string publicKey, out PasetoToken pasetoToken) { try { var pasetoTokenValidationResult = new PasetoBuilder() .Use(ProtocolVersion.V1, Purpose.Public) .WithKey(Convert.FromBase64String(publicKey), Encryption.AsymmetricPublicKey) .Decode(token); pasetoToken = pasetoTokenValidationResult.Paseto; return pasetoTokenValidationResult.IsValid; } catch (Exception ex) { pasetoToken = null; return false; } } }Görüldüğü üzere hem local token hem de puclic token olmak üzere iki farklı token yapısı üzerinde çalışma sergileyen helper tasarlamış bulunuyoruz.
- Adım 2 Uygulamada PASETO Authentication’ın Handle Edilmesi
Uygulamada authentication yapılandırması olarak PASETO konfigürasyonunu gerçekleştirebilmek için de aşağıdaki gibi bir handler sınıfı oluşturulabilir;public class PasetoAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions> { readonly PasetoHandler _pasetoHandler; public PasetoAuthenticationHandler( IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, PasetoHandler pasetoHandler) : base(options, logger, encoder) { _pasetoHandler = pasetoHandler; } protected override Task<AuthenticateResult> HandleAuthenticateAsync() { if (!Request.Headers.ContainsKey("Authorization")) return Task.FromResult(AuthenticateResult.Fail("Authorization header not found.")); string? publicKey = null; if (Request.Headers.ContainsKey("PublicKey")) publicKey = Request.Headers["PublicKey"] .ToString(); string token = Request.Headers["Authorization"] .ToString() .Replace("Bearer ", string.Empty); if (publicKey is null) { if (_pasetoHandler.ValidateLocalToken(token, out PasetoToken pasetoToken)) { var _claims = pasetoToken.Payload.Select(c => new { c.Key, c.Value }); var _footer = pasetoToken.Footer; var claims = new[] { new Claim(ClaimTypes.Name, pasetoToken.Payload["name"].ToString()), new Claim(ClaimTypes.Role, pasetoToken.Payload["role"].ToString()) }; var identity = new ClaimsIdentity(claims, "Paseto"); var principal = new ClaimsPrincipal(identity); var ticket = new AuthenticationTicket(principal, "Paseto"); return Task.FromResult(AuthenticateResult.Success(ticket)); } else return Task.FromResult(AuthenticateResult.Fail("Invalid token.")); } else { if (_pasetoHandler.ValidatePublicToken(token, publicKey, out PasetoToken pasetoToken)) { var _claims = pasetoToken.Payload.Select(c => new { c.Key, c.Value }); var _footer = pasetoToken.Footer; var claims = new[] { new Claim(ClaimTypes.Name, pasetoToken.Payload["name"].ToString()), new Claim(ClaimTypes.Role, pasetoToken.Payload["role"].ToString()) }; var identity = new ClaimsIdentity(claims, "Paseto"); var principal = new ClaimsPrincipal(identity); var ticket = new AuthenticationTicket(principal, "Paseto"); return Task.FromResult(AuthenticateResult.Success(ticket)); } else return Task.FromResult(AuthenticateResult.Fail("Invalid token.")); } } }Burada görüldüğü üzere gelen istek neticesinde bir token var mı yok mu kontrol edilmekte, olduğu taktirde gerekli authentication çalışması yürütülmektedir. Tabi biryandan da bu token’la birlikte public key geliyorsa eğer bu token’ın public token olduğu anlaşılmakta ve ona göre validation davranışı şekillenmektedir.
Ayrıca 36–37. ve 56–57. satırlarındaki claim yapılandırmalarına odaklanılırsa, bunların JWT yapılandırmasından alışılagelen
NameClaimTypeveRoleClaimTypekonfigürasyonlarına karşılık geldiği söylenebilir. - Adım 3 Bu Yapıların Uygulamada Kullanılması
Ve son olarak yukarıda geliştirilen bu yapılar aşağıdaki gibi uygulamada kullanılabilir ve böylece uygulamanın PASETO ile authentication süreci başlatılabilir;var builder = WebApplication.CreateBuilder(args); builder.Services.AddSingleton<PasetoHandler>(); builder.Services.AddAuthentication("Paseto") .AddScheme<AuthenticationSchemeOptions, PasetoAuthenticationHandler>("Paseto", null); builder.Services.AddAuthorization(); var app = builder.Build(); app.UseAuthentication(); app.UseAuthorization(); app.MapGet("/", (HttpContext context, PasetoHandler pasetoHandler) => { return Results.Ok(); }) .RequireAuthorization(p => p.RequireRole("mod")); app.MapGet("/get-local-token", (PasetoHandler pasetoHandler) => { return pasetoHandler.GenerateLocalToken(); }); app.MapGet("/get-public-token", (PasetoHandler pasetoHandler) => { return pasetoHandler.GeneratePublicToken(); }); app.Run();Burada da 4 ile 6. satır aralığına odaklanılırsa, PASETO şemasında authentication servisi eklenmekte ve bu sırada
AddSchemefonksiyonuyla da biraz önce oluşturulanPasetoAuthenticationHandlerdevreye sokulmaktadır. Bu yapılandırma neticesinde artık uygulamanın kimlik doğrulama kontrolü PASETO davranışı üzerinden yürütülmektedir. Bu süreçte test amaçlı görüldüğü üzere üç adet endpoint tanımlanmıştır ve her biri adından da anlaşılacağı üzere mahiyetine uygun işlevler gerçekleştirmektedir.
İşte bu kadar… Bir uygulamada PASETO üzerinden authentication yapılandırması aşağı yukarı bu şekilde seyredecektir…
PASETO ve JWT Arasında Seçim Yapmak
Yazımız boyunca hem PASETO’nun hem de JWT’nin kendine özgü avantaj ve dezavantajlarını değerlendirmiş bulunuyoruz. Tüm bunları hesaba kattığımızda aşikardır ki hangisinin seçileceği keyfilikten ziyade ihtiyaç ve önceliklere bağımlılık göstermektedir.
Özellikle güvenlik açıklarına karşı bir hassasiyet gösteriyor ve kritik bir çalışma sergiliyorsanız tabi ki de JWT’ye nazaran PASETO daha uygun olacaktır.
Ayrıca iş mantığı gereği farklı mimarisel gereksinimlerin söz konusu olduğu durumlarda da PASETO, local token ve public token özellikleri sayesinde daha uyumlu bir davranış sergileyebilir. Özellikle üretilen token üzerinden oturum yönetimi gibi geleneksel bir durum için PASETO oldukça idealdir diyebiliriz.
Tüm bunların dışında PASETO henüz yeni bir standart olduğu için her geçen an istikrarlık büyüme gerçekleştirsede küçük bir ekosisteme sahiptir. Her platform için uygun kütüphanelerin bulunması ve bunların yapılandırma kurallarına dair içeriklere erişilmesi JWT kadar pratik olmayabilir.
İşte bu sebeplerden dolayı uygulamalarınızın güvenliği için en doğru yaklaşım ve teknolojileri ihtiyaç analizleriniz neticesinde iradenizle seçmeniz gerekmektedir.
Nihai olarak;
Artık şunu söyleyebiliriz ki günümüz de her şeyde olduğu gibi güvenlik tehditlerinde de bir değişim ve hatta gelişim söz konusudur. Bundan dolayı uygulamalarımızda etkili kimlik doğrulama mekanizmalarına sürekli artan bir ihtiyaç durumu vardır diyebiliriz. Hatta kuantum bilgisayarlar hali hazırda mevcut olan kriptografik algoritmaları kırma konusunda oldukça mahir oldukları için artık bizlerin kuantum dirençli geliştirilmiş olan algoritmaları benimsememiz gerekmektedir. İşte PASETO, güvenlik odaklı olmasından kaynaklı bu kritik neticesinde JWT’ye nazaran daha değerli bir konuma gelmektedir.
İhtiyaç halinde kullanmanızı ve faydalanmanızı dilerim 🙂
İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…
Not : Örnek çalışmaya aşağıdaki github adresinden erişebilirsiniz.
https://github.com/gncyyldz/Token_Based_Authentication_With_PASETO



Çok güzel bir yazı olmuş hocam elinize saglık