Bir JSON Web Token gerçekte ne içerir

Bir JWT'nin içinde ne olduğu; başlık, yük ve imza açıklanıyor, standart talepler sıralanıyor ve bir tokenı Base64url ile çözmenin neden onu doğrulamakla aynı şey olmadığı anlatılıyor.

Bir JSON Web Token (JWT), noktalarla birleştirilmiş üç metin parçasıdır. Her parça Base64url ile kodlanmıştır ve ilk ikisi düpedüz JSON'dur. Tokena sahip olan herkes içindekini okuyabilir. Normal bir JWT'de hiçbir şey şifreli değildir.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9      <- header
eyJzdWIiOiIxMjM0NSIsIm5hbWUiOiJBbGV4In0   <- payload
dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFW     <- signature

Noktalarla birleştirildiğinde bu, bir isteğe yapıştırdığınız tokendır. İlk iki parçayı çözün, karşınıza sıradan JSON çıkar:

{ "alg": "HS256", "typ": "JWT" }
{ "sub": "12345", "name": "Alex" }

Gerçek bir yük genellikle zaman damgaları ve bir hedef kitle de taşır:

{
  "iss": "https://auth.example.com",
  "sub": "12345",
  "aud": "api.example.com",
  "iat": 1756000000,
  "exp": 1756003600,
  "roles": ["editor"]
}

Başlık

Başlık, tokenın nasıl imzalandığını tarif eder. En çok iki alan önem taşır:

  • alg, imzalama algoritmasının adıdır; örneğin HS256 (ortak bir sırla, SHA-256 üzerinden HMAC) ya da RS256 (SHA-256 ile RSA imzası; özel anahtarla imzalanır, açık anahtarla denetlenir).
  • Anahtar kimliği olan kid isteğe bağlıdır ve alıcıya birkaç anahtardan hangisinin kullanıldığını söyler; böylece var olan tokenları kırmadan anahtarlar döndürülebilir.

typ genellikle yalnızca JWT değerini taşır ve güvenlik açısından bir anlamı yoktur.

Yük ve talepleri

Yük, taleplerden oluşan bir JSON nesnesidir; talep, şartnamenin tokenın öznesi hakkındaki ifadelere verdiği addan başka bir şey değildir. Bazı talep adları standartta kayıtlıdır ve hepsi kısadır:

TalepAdıNe tutar
issVerenTokenı kimin oluşturduğu, genellikle bir URL ya da hizmet adı
subÖzneTokenın kim hakkında olduğu, genellikle sabit bir kullanıcı kimliği
audHedef kitleTokenın kimin için tasarlandığı; böylece bir hizmet başkası için verilmiş tokenları reddedebilir
expSon geçerlilik zamanıTokenın bundan sonra reddedilmesi gereken an
nbfŞundan önce değilTokenın bundan önce reddedilmesi gereken an
iatVerilme zamanıTokenın ne zaman oluşturulduğu
jtiJWT kimliğiBenzersiz bir tanımlayıcı; tek tek tokenları izlemek ya da engellemek için işe yarar

exp, nbf ve iat değerlerinin hepsi NumericDate değerleridir, yani 1 Ocak 1970 UTC tarihinden bu yana geçen saniye sayısıdır. Bunlar saniyedir, milisaniye değil. Bir zaman damgası 57.000 yılına denk gelen bir tarihe çözülüyorsa milisaniye okuyorsunuz demektir ve bine bölmeniz gerekir.

Yükteki diğer her şey, tokenı verenin oraya koyduğu şeydir: roller, izinler, bir e-posta adresi, bir kiracı kimliği. Özel bir talep adı bir gün kayıtlı bir adla çakışabileceği için, token verenler kendi adlarını çoğu zaman URL benzeri bir önekle ad alanına alır.

İmza

İmza, ilk iki parçanın nokta ile birleştirilmiş tam metni üzerinden, başlıktaki algoritma ve bir anahtar kullanılarak hesaplanır. Başlığın ya da yükün tek bir karakterini değiştirin, imza artık tutmaz.

HS256 ile aynı sır hem imzalar hem doğrular, dolayısıyla bir tokenı denetleyebilen herkes yenisini de basabilir. RS256 ya da ES256 ile özel anahtar imzalar, açık anahtar doğrular; bir kimlik sağlayıcının, hiçbir sır tutmadan birçok ayrı hizmetin doğrulayabileceği tokenlar dağıtmasını sağlayan da budur.

Base64url şifreleme değildir

Base64url, Base64 ile aynı fikirdir ve iki farkı vardır: sonuç URL'lerde güvenli olsun diye + ve / yerine - ve _ kullanır ve sondaki = dolgusu genellikle atılır. Bir kodlamadır, yani baytları metin olarak yazmanın geri alınabilir bir yoludur. Hiçbir biçimde gizlilik sağlamaz.

Bu yüzden bir JWT yüküne asla hassas bir şey koymayın: parola yok, kart numarası yok, kullanıcı hakkında iç notlar yok. Tokenı elinde tutan kişinin ve onu bir günlük dosyasından ya da tarayıcı deposundan okuyan herkesin her alanı görebildiğini varsayın.

Yükü gerçekten şifreleyen ayrı bir biçim vardır, adı JWE'dir ve üç değil beş parçalıdır. Tokenınız üç parçalıysa imzalanmıştır ve okunabilirdir.

Çözmek doğrulamak değildir

Asıl önemli nokta budur. Bir çözücü size tokenın ne dediğini gösterir. Tokenın gerçek olup olmadığını söylemez. Doğrulama ayrı bir adımdır ve anahtar gerektirir.

Bir tokena gerçekten güvenmek için sunucunun şunları yapması gerekir:

  1. İmzayı doğru anahtara karşı denetlemek.
  2. Tokenın içindeki alg alanına güvenmek yerine beklediği algoritmada ısrar etmek. Bunu atlamaktan iki klasik saldırı doğar: alg alanını none yapıp imzayı silmek ve bir hizmetin RSA açık anahtarını alıp onu bir HMAC sırrı olarak kullanarak RS256 doğrulayıcısını HS256 çalıştırmaya kandırmak.
  3. exp alanını, varsa nbf alanını denetlemek ve küçük bir saat kaymasına izin vermek.
  4. iss ve aud alanlarının bu hizmetin beklediğiyle uyuştuğunu denetlemek.

Talepler ancak bundan sonra bir anlam taşır. O ana kadar yük, ağ üzerinden gelmiş, doğrulanmamış bir dizeden ibarettir.

Pratik notlar

  • Tokenlar genellikle bir HTTP başlığında Authorization: Bearer <token> biçiminde gönderilir, o yüzden yükü küçük tutun. Eklediğiniz her talep her istekle birlikte gönderilir.
  • İmzalanmış bir token süresi dolana kadar geçerli kalır. Bir tokenı iptal etmenin yerleşik bir yolu yoktur; erişim tokenlarının kısa, çoğu zaman dakikalarla ölçülen ömürleri olmasının ve yenilerini almak için ayrı bir yenileme tokenı kullanılmasının nedeni budur.
  • Bir token bozuk görünüyorsa önce noktaları sayın. İki nokta ve üç parça normal, imzalanmış bir JWT demektir. Bir terminalden yapıştırılmış kırpılmış bir token, "geçersiz imza" hatasının çok yaygın bir nedenidir.
  • Hata ayıklarken bir tokenı, süresini, öznesini ya da rollerini okumak için çözmek tümüyle güvenlidir ve hiçbir sır gerektirmez. Yükün varlık nedeni tam olarak budur.