İçeriğe geç
gelistiriciaraclari

JWT'nin Yapısı ve Hata Ayıklama Rehberi

· 9 dk okuma

Bir kapı kilidinde asılı duran anahtarların yakın çekimi
Fotoğraf: Matt Clark / Unsplash

JWT (JSON Web Token, RFC 7519) bugün API kimlik doğrulamasından mobil uygulama oturumlarına, tek oturum açma (SSO) akışlarından mikroservisler arası yetkilendirmeye kadar hemen her yerde kullanılıyor; formatının basitliği ve neredeyse her dilde hazır bir kütüphanesinin bulunması bu yaygınlığın başlıca sebeplerindendir. Token rastgele bir karakter dizisi gibi göründüğü için birçok geliştirici onun şifreli olduğunu düşünür; oysa standart imzalı bir JWT yalnızca kodlanmıştır (encoded), şifrelenmiş (encrypted) değildir. Bu yanılgı, hassas verilerin doğrudan token içine konmasından zaman alanlarının yanlış yorumlanmasına kadar birçok pratik hataya yol açar.

Bir JWT'yi elle çözmek zor değildir, ama header/payload ayrımını, zaman alanlarının hangi birimde tutulduğunu ve imzanın gerçekte neyi garanti ettiğini bilmeden yapılan bir hata ayıklama genelde yanlış sonuca varır. Bu rehber, JWT ile ilk kez çalışan bir geliştiricinin de bir API entegrasyonunda 'invalid signature' ya da 'token expired' hatasıyla uğraşan deneyimli bir ekibin de ihtiyaç duyacağı temel kavramları tek yerde toplar. JWT çöz aracı bir token'ı üç parçasına ayırıp header ve payload'ı okunabilir JSON olarak gösterir; exp, iat, nbf alanlarını tarihe çevirir, alg değeri none ise uyarır ve isterseniz imzayı HS256/384/512 için bir gizli anahtarla, RS256/384/512, PS256, ES256/384 için bir PEM genel anahtar ya da JWKS JSON'ı ile doğrular. Tüm işlem tarayıcınızda WebCrypto API üzerinden yapılır; token hiçbir yere ağ üzerinden gönderilmez, bu da özellikle üretim ortamına ait gerçek bir token'ı incelerken önemlidir.

$ araçJWT ÇözücüJWT'nin başlığını, içeriğini ve imzasını inceleyin.

JWT'nin Yapısı: Header.Payload.Signature

Bir JWT, nokta ile ayrılmış üç bölümden oluşur: header, payload ve signature. Header genelde {"alg":"HS256","typ":"JWT"} gibi kullanılan imza algoritmasını belirtir; payload uygulamaya özel verileri (claim'leri) taşır; signature ise header ve payload'ın birleşiminin belirtilen algoritmayla imzalanmış halidir. Üç bölüm de ayrı ayrı Base64URL ile kodlanır ve nokta karakteriyle birleştirilir. RFC 7519, sub (subject), iss (issuer), aud (audience) gibi bazı standart claim isimleri de tanımlar; bunlar zorunlu değildir ama farklı sistemler arasında ortak bir sözlük sağlar.

Örneğin eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0In0.SIGNATURE gibi bir token, ilk noktaya kadar header'ı, ikinci noktaya kadar payload'ı, son bölüm ise imzayı taşır. JWT çöz aracına bir token yapıştırdığınızda bu üç parça otomatik ayrılır ve header ile payload okunabilir JSON olarak gösterilir; imzayı doğrulamadan önce bile hangi algoritmanın beklendiğini ve token'ın kime ait olduğunu görebilirsiniz.

Base64URL Kodlama ve JWT Neden Şifreli Değildir

Base64URL, standart Base64'ün '+' ve '/' karakterlerini URL'de sorun çıkarmayacak '-' ve '_' ile değiştiren, dolgu (padding) karakterlerini genelde atlayan bir varyantıdır (RFC 4648 §5). Kodlama tersine çevrilebilir bir dönüşümdür, şifreleme değildir; yani header ve payload'ı herkes anahtar olmadan çözüp okuyabilir. Bu işlemi elle doğrulamak isterseniz base64 decode aracına header ya da payload parçasını yapıştırmanız yeterlidir; standart Base64 çözücülerin bazıları '-' ve '_' karakterlerini tanımadığından, önce bunları '+' ve '/' ile değiştirmeniz, eksik dolguyu tamamlamanız gerekebilir.

Bu, JWT'lerin en çok yanlış anlaşılan noktasıdır: token 'rastgele' göründüğü için şifreli sanılır, oysa imza (signature) yalnızca içeriğin değiştirilmediğini garanti eder, içeriği gizlemez. Gizlilik gerekiyorsa JWE (JSON Web Encryption) gibi ayrı bir standart kullanılmalıdır; sıradan imzalı bir JWT'nin payload'ı herkese açıktır. Tarayıcının geliştirici araçlarındaki Ağ (Network) sekmesinden bir isteğin Authorization başlığını kopyalayıp incelemek, bu farkı görmenin en hızlı yoludur.

exp, iat, nbf: Zaman Alanları ve Saat Kayması

exp (expiration) token'ın geçersiz sayılacağı, iat (issued at) üretildiği, nbf (not before) ise geçerli olmaya başlayacağı zamanı Unix epoch (saniye) olarak tutar; milisaniye değil saniye olması sık karışan bir noktadır, çünkü JavaScript'in kendi Date nesnesi milisaniye bekler. Bir token'ın süresinin dolup dolmadığını anlamak için exp değerini geçerli zamanla karşılaştırmak yeterlidir; unix timestamp dönüştürücü ile bu epoch değerini okunabilir tarihe çevirebilirsiniz.

Pratikte sık karşılaşılan bir sorun saat kaymasıdır (clock skew): sunucu ile istemcinin saatleri birkaç saniye farklıysa, henüz süresi dolmamış bir token 'expired' ya da henüz başlamamış bir token 'not yet valid' olarak reddedilebilir. Çoğu JWT kütüphanesi bu yüzden birkaç saniyelik bir tolerans (leeway) payı bırakır; kendi doğrulama kodunuzu yazıyorsanız bu payı eklemeyi unutmayın. Konteyner tabanlı ortamlarda sunucu saatinin NTP ile senkron olmaması, özellikle kısa ömürlü (birkaç dakikalık) access token'larda beklenmedik ve aralıklı 'expired' hatalarına yol açan, tespit edilmesi zor bir kaynak olabilir.

alg: none ve Algoritma Karışıklığı Saldırıları

JWT standardı 'none' adında bir algoritma tanımlar; bazı kütüphanelerin eski sürümleri header'da alg:none gören bir token'ı imza kontrolü yapmadan kabul ediyordu. Saldırgan payload'ı istediği gibi değiştirip alg'ı none yaparsa, savunmasız bir doğrulayıcı bunu geçerli sayabilir. Modern kütüphaneler varsayılan olarak none'ı reddeder, ama sunucu kodunda beklenen algoritma açıkça belirtilmiyorsa risk sürer; özellikle 'decode et ve algoritmayı token'ın kendisinden oku' şeklindeki gevşek bir yaklaşım bu saldırıyı doğrudan mümkün kılar.

Daha ince bir saldırı algoritma karışıklığıdır (algorithm confusion): sunucu RS256 (asimetrik) bekliyorken saldırgan header'ı HS256 (simetrik) yapıp herkesin bildiği RSA genel anahtarını HMAC gizli anahtarı gibi kullanarak sahte bir imza üretir. RSA genel anahtarı tanım gereği herkese açık olduğundan (genelde bir JWKS uç noktasında ya da sertifikada yayınlanır), saldırgan bu anahtarı kolayca elde edip HMAC'in gizli anahtarı yerine geçirebilir. Zafiyetli kod imzayı 'anahtar var mı' diye kontrol edip hangi algoritmanın kullanıldığını sunucu tarafında sabitlemezse bu saldırı çalışır. Doğru savunma, doğrulama kodunda beklenen algoritmayı (mümkünse tek bir sabit değer olarak) tutmak ve token header'ındaki alg alanına asla güvenmemektir.

İmza Doğrulama: HMAC ve RSA/ECDSA Farkı

HS256/384/512 simetriktir: token'ı imzalayan ve doğrulayan taraf aynı gizli anahtarı bilir, genelde tek bir backend servisinin kendi ürettiği token'ları kendi doğruladığı senaryolarda uygundur; anahtar sızarsa hem imzalama hem doğrulama yeteneği ele geçmiş olur. RS256/384/512, PS256 ve ES256/384 ise asimetriktir: özel anahtarla imzalanır, herkese açık olabilecek genel anahtarla doğrulanır; bu, token'ı birden fazla servisin (mikroservis mimarisi, üçüncü taraf API'ler) bağımsızca ve gizli bir anahtar paylaşmadan doğrulayabilmesi gereken durumlarda tercih edilir. ES256/384 (eliptik eğri tabanlı), RSA ailesine göre çok daha kısa imzalar ürettiği için token boyutunun önemli olduğu senaryolarda giderek daha yaygın kullanılıyor.

JWT çöz aracı her iki türü de destekler: HMAC ailesinde bir gizli anahtar, RSA/ECDSA ailesinde bir PEM genel anahtar ya da JWKS JSON'ı yapıştırarak imzayı tarayıcınızda doğrulayabilirsiniz, token hiçbir zaman ağ üzerinden başka bir yere gönderilmez. Bu, özellikle bir müşteriden gelen ya da üretim ortamına ait gerçek bir token'ı hata ayıklamak için incelerken, token'ı üçüncü taraf bir siteye yapıştırmanın getireceği riski ortadan kaldırır.

JWT'ye Asla Koymamanız Gerekenler

Payload herkese açık olduğundan parola, kredi kartı numarası, TC kimlik numarası gibi hassas verileri doğrudan claim olarak koymak ciddi bir veri sızıntısıdır; token'ı ele geçiren (ya da sadece tarayıcının geliştirici araçlarını açan) herkes bu bilgiyi okuyabilir. Aynı şekilde büyük veri (uzun listeler, dosya içeriği, geniş yetki tabloları) koymak her istekte gönderilen header boyutunu şişirir ve bazı proxy/sunucu yapılandırmalarında başlık boyutu sınırına takılan gizemli 431 hatalarına yol açabilir.

Genel kural: JWT'ye yalnızca kimliği doğrulamak ve yetkilendirmek için gereken en az bilgiyi, örneğin kullanıcı kimliği, rol ve süre gibi alanları koyun; kalan her şeyi sunucu tarafında bu kimlikten yola çıkarak veritabanından sorgulayın. Hash oluştur aracıyla bir claim'in beklenen değerle eşleşip eşleşmediğini karşılaştırmak, hassas veriyi token içine koymadan bütünlük kontrolü yapmanın bir yoludur. Token'ı bir kez üretip uzun süre değiştirmemek yerine, kritik yetki değişikliklerinde (örneğin bir kullanıcı yönetici yetkisini kaybettiğinde) eski token'ların geçersiz sayılabileceği bir mekanizma (kısa ömür + refresh, ya da bir iptal listesi) kurmak da güvenlik açısından önemlidir.

Sıkça sorulan sorular

JWT şifreli mi, biri token'ımı görürse içeriğini okuyabilir mi?

Hayır, standart imzalı bir JWT şifreli değildir; header ve payload Base64URL ile kodlanmıştır ve anahtar olmadan da herkes tarafından okunabilir, imza yalnızca değiştirilmediğini garanti eder, içeriği gizlemez. Gizlilik gerekiyorsa ayrı bir JWE standardı kullanılmalıdır.

Token 'expired' diyor ama süresi dolmamış gibi görünüyor, neden?

Sunucu ile istemci saatleri arasında birkaç saniyelik fark (clock skew) buna yol açabilir; exp değerini unix timestamp dönüştürücü ile kontrol edip sunucu saatinizin doğru olduğundan emin olun.

alg: none neden tehlikeli?

Bazı eski kütüphaneler bu değeri gören token'ı imza kontrolü yapmadan kabul ediyordu; saldırgan payload'ı değiştirip alg'ı none yaparak sahte bir token üretebiliyordu, bu yüzden modern kütüphaneler bunu varsayılan olarak reddeder ve açıkça izin verilmedikçe kabul etmez, ayrıca beklenen algoritmayı sunucu kodunda sabit tutmak ek bir güvence sağlar.

Refresh token da JWT olmalı mı?

Zorunlu değildir; birçok sistemde refresh token opak, rastgele üretilmiş ve sunucu tarafında bir veritabanında tutulan bir dizedir, böylece gerektiğinde tek tek ya da toplu olarak iptal edilebilir, bu da bir JWT'nin kendi kendine yetmesinden farklıdır ve daha güçlü bir kontrol sağlar.

Diğer rehberler