Bölüm 23
JWT Security
Bölüm 12 (kimlik doğrulama), Bölüm 13 (HMAC, imzalama, CSPRNG, sırlar), Bölüm 16 (oturum — durumlu alternatif), Bölüm 22 (API kimlik). İleri referanslar: Bölüm 24 (OAuth/OIDC), Bölüm 25 (imzalama anahtarı).
JWT'yi, "imza ile bütünlüğü korunan, taşıyıcı bir kimlik belgesi" olarak kavrayacak; imza doğrulamasını atlatan üç saldırı sınıfını (alg:none, algoritma karışıklığı, zayıf sır) ve tek en önemli savunmayı — algoritmayı token'dan okuma, açıkça beyaz listele — göreceksiniz.
1. Giriş
JWT (JSON Web Token), modern API ve mikroservis mimarilerinin en yaygın kimlik taşıyıcısıdır. Durumsuz (stateless) doğası sayesinde sunucunun oturum saklamasına gerek kalmaz: token'ın kendisi kimlik iddialarını (claim) taşır ve bir imzayla bütünlüğü korunur. Sunucu, imzayı doğrulayarak token'ın kurcalanmadığından emin olur ve içindeki iddialara (kullanıcı kimliği, rol, süre) güvenir.
Ama bu model kritik bir noktaya dayanır: imza her şeydir. Bir saldırgan geçerli bir imza üretebilir veya doğrulamayı atlatabilirse, iddiaları serbestçe belirler — istediği kullanıcı olur, kendini admin yapar. JWT güvenliği, neredeyse tamamen "imza doğrulamasının doğru yapılması" sorunudur. Ve bu doğrulama, sezgiye aykırı biçimlerde kırılabilir.
JWT saldırı sınıflarını tanımlayan olay, Tim McLean'in 2015'teki açıklamasıdır (Bölüm 11): birçok popüler kütüphanede — php-jwt dahil — imza doğrulamasını atlatan iki temel kusur buldu (alg:none ve RS256→HS256 karışıklığı). Bu bulgu on yıldan fazla süre önce yayımlandı, kütüphaneler haftalar içinde yamalandı — ama aynı iki hata hâlâ yeni kodda ve yeni kimlik servislerinde ortaya çıkıyor. Bu yüzden bu bölüm, mekanizmayı derinlemesine anlamaya odaklanır.
2. Temel Teori
JWT yapısı. Bir JWT, nokta ile ayrılmış üç base64url bölümden oluşur: header.payload.signature.
- Header: alg (imza algoritması: HS256, RS256, ES256...), typ, opsiyonel kid (anahtar kimliği).
- Payload: iddialar (claim) — sub (özne), iss (yayınlayan), aud (hedef kitle), exp (son kullanma), nbf (geçerlilik başlangıcı), iat, ve özel iddialar (rol vb.).
- Signature: header + payload üzerinden hesaplanır.
Kritik uyarı: payload şifreli DEĞİLDİR. Base64url yalnızca bir kodlamadır (Bölüm 13); herkes payload'ı çözüp okuyabilir. JWT gizlilik değil bütünlük sağlar. Payload'a asla sır (parola, API anahtarı, hassas veri) koyulmaz. (Şifreleme gerekiyorsa JWE ayrı bir standarttır.)
İmza modeli — iki tür:
| Tür | Algoritma | Anahtar | İmzalayan / Doğrulayan |
|---|---|---|---|
| Simetrik (HMAC) | HS256/384/512 | Tek gizli anahtar | Aynı sır imzalar ve doğrular |
| Asimetrik (RSA/ECDSA) | RS256, ES256... | Özel + açık anahtar çifti | Özel anahtar imzalar, açık anahtar doğrular |
Asimetrikte açık anahtar herkese verilebilir (doğrulama için); imza yalnızca gizli özel anahtarla üretilir. Simetrikte tek sır hem imzalar hem doğrular — bu sır gizli kalmalıdır.
Üç saldırı sınıfı — hepsi imza doğrulamasını atlatır:
1. alg:none: JWT standardı, "bütünlüğü başka yolla doğrulanmış" token'lar için bir none algoritması tanımlar (imzasız). Bazı kütüphaneler, {"alg":"none"} başlıklı ve boş imzalı bir token'ı "geçerli/doğrulanmış" sayardı. Sonuç: saldırgan istediği payload'la token üretir, imza gerekmez.
2. Algoritma karışıklığı (RS256→HS256): Sunucu RS256 (asimetrik) beklerken, saldırgan başlığı HS256 (simetrik) yapar. Kütüphane algoritmayı token başlığından seçtiği için, HMAC doğrulaması yapar ve uygulamanın verdiği anahtarı — yani RSA açık anahtarını — HMAC sırrı olarak kullanır. Açık anahtar bilindiğinden, saldırgan o açık anahtarla HS256 imzası üretip doğrulamayı geçer. Özel anahtara hiç ihtiyaç yoktur.
3. Zayıf HMAC sırrı: HS256 sırrı düşük entropili/sözlükte bir değerse, saldırgan onu çevrimdışı kaba kuvvetle bulur (Bölüm 13) ve geçerli token üretir.
Ek sınıf — kid enjeksiyonu: kid başlığı saldırgan-kontrollüdür; anahtar aramada güvensiz kullanılırsa (ham SQL, dosya yolu, komut), SQLi (Bölüm 6) / path traversal (Bölüm 20) / komut enjeksiyonuyla (Bölüm 8) saldırgan kullanılacak anahtarı kontrol eder.
Tek en önemli savunma. Üç sınıfın ortak kök nedeni: kütüphanenin algoritmayı, saldırgan-kontrollü alg başlığından seçmesi. Doğru zihinsel model: "kütüphaneye, beklediği algoritmayı söyle; onu tespit etmesini isteme." Algoritmayı açıkça beyaz listelemek, alg:none ve algoritma karışıklığını tek hamlede kapatır.
3. Mimarisel Bakış
JWT DOĞRULAMA:
İstek → Authorization: Bearer header.payload.signature
↓
[ İmza doğrula ] ← DOĞRU: algoritma UYGULAMADA sabit (beyaz liste)
↓ ← YANLIŞ: algoritma TOKEN başlığından (saldırgan-kontrollü)
[ Claim doğrula: exp, nbf, iss, aud ]
↓
İddialara güven (sub, rol)
alg:none SALDIRISI:
Saldırgan → {"alg":"none"} + istediği payload + BOŞ imza
↓ kütüphane "none" görüp doğrulamayı ATLARSA
→ herhangi bir kimlik/rol taklit edilir
ALGORİTMA KARIŞIKLIĞI (RS256→HS256):
Sunucu RS256 bekler, açık anahtarı doğrulamaya verir
Saldırgan → başlığı HS256 yapar, AÇIK ANAHTARLA HMAC imzası üretir
↓ kütüphane algoritmayı başlıktan seçip HMAC doğrularsa
→ açık anahtar "sır" olarak kullanılır (bilinen değer) → imza geçer
SAVUNMA:
JWT::decode($jwt, new Key($key, 'HS256')) ← algoritma SABİT
→ başlıktaki alg dikkate ALINMAZ → none/confusion imkânsız
Kritik gözlem: güvenli doğrulamada algoritma uygulama tarafından sabitlenir; token başlığındaki alg bir tercih değil, olsa olsa iddiadır ve güvenilmez.
4. Güvensiz Kod
Gerçekçi bir JWT doğrulama (elle ve kütüphaneyle yanlış kullanım):
<?php
// ⚠️ GÜVENSİZ — imza doğrulaması atlatılabilir
declare(strict_types=1);
// 1) Algoritmayı TOKEN'dan seçmek (none + confusion)
$header = json_decode(base64_decode(explode('.', $jwt)[0]), true);
$alg = $header['alg']; // saldırgan-kontrollü!
if ($alg === 'none') {
$payload = decodePayload($jwt); // imza doğrulamadan kabul (alg:none)
} elseif ($alg === 'HS256') {
verifyHmac($jwt, $publicKey); // RS256 beklenirken HS256 + açık anahtar (confusion)
}
// 2) Zayıf HMAC sırrı
$secret = 'secret'; // sözlükte; çevrimdışı kırılır (Böl. 13)
JWT::decode($jwt, new Key($secret, 'HS256'));
// 3) Claim doğrulaması yok
$claims = decodePayload($jwt);
$userId = $claims['sub']; // exp/iss/aud kontrol edilmiyor
// 4) kid'i ham sorguda kullanmak (kid injection)
$kid = $header['kid'];
$key = $pdo->query("SELECT k FROM keys WHERE id = '$kid'")->fetch(); // SQLi (Böl. 6)
5. Açığın Analizi
Algoritmayı token'dan seçmek — Tüm JWT felaketlerinin anası. $alg = $header['alg'] saldırgan-kontrollüdür (Bölüm 1). Buna göre dallanmak:
- alg:none: Saldırgan başlığı none yapıp boş imzayla gönderir; kod imza doğrulamadan kabul eder. İstediği sub/rol ile herkesi taklit eder.
- Algoritma karışıklığı: Sunucu RS256 bekler ve $publicKey'i doğrulamaya verir. Saldırgan başlığı HS256 yapar; kod HMAC doğrulaması yapıp açık anahtarı sır olarak kullanır. Açık anahtar bilindiğinden saldırgan geçerli imza üretir — özel anahtar gerekmez.
$secret = 'secret' — Zayıf HMAC sırrı. Düşük entropili sır, çevrimdışı kaba kuvvetle (Bölüm 13) hızla bulunur; saldırgan sonra geçerli token üretir. HMAC sırrı, CSPRNG ile üretilmiş ≥256 bit olmalıdır.
Claim doğrulaması yok — exp kontrol edilmediğinden süresi dolmuş token kabul edilir; iss/aud kontrol edilmediğinden başka bir servis/kiracı için üretilmiş token bu serviste geçer (token confusion). İmza geçerli olsa bile iddialar doğrulanmalı.
kid'i ham sorguda kullanmak — kid enjeksiyonu. kid saldırgan-kontrollüdür; ham SQL'e konunca SQLi (Bölüm 6) olur; saldırgan UNION ile kendi bildiği bir anahtarı "doğru anahtar" olarak döndürüp geçerli token üretir. kid dosya yolu/komutta da benzer enjeksiyonlara yol açar.
Kök sorun: doğrulama, saldırgan-kontrollü başlığa (özellikle alg ve kid) güveniyor, sır zayıf ve claim'ler doğrulanmıyor. Çözüm: algoritmayı sabitle, sırrı güçlendir, claim'leri doğrula, kid'i güvenilmez say.
6. Hacker Bakış Açısı
Saldırgan JWT'yi nasıl hedefler?
Token'ı ayrıştırır. JWT base64url olduğundan payload'ı anında okur (şifresiz!). İçindeki iddiaları (sub, role, iss, aud, exp) ve başlığı (alg, kid) inceler. Payload'da sır varsa (yaygın hata) doğrudan toplar.
alg:none dener. Başlığı {"alg":"none"} yapıp imzayı boşaltır (header.payload.). Kütüphane/kod bunu kabul ederse, istediği rolle token üretir — anında yetki yükseltme.
Algoritma karışıklığı dener. Sunucu RS256 kullanıyorsa (JWT'de alg:RS256), açık anahtarı bulur (genelde /.well-known/jwks.json veya dokümantasyondan açıkça yayınlanır). Başlığı HS256 yapıp o açık anahtarı HMAC sırrı olarak kullanarak imzalar. Kod algoritmayı başlıktan seçiyorsa doğrulama geçer.
Sırrı kaba kuvvetle dener. HS256 ise, yakaladığı geçerli bir token'ı çevrimdışı araçlarla (sözlük/kaba kuvvet) sırra karşı dener. Sır zayıfsa bulur ve token üretir.
kid'i istismar eder. kid bir anahtar aramada kullanılıyorsa, oraya SQLi/path traversal/komut enjekte edip kullanılacak anahtarı kontrol etmeyi dener (kendi bildiği bir anahtara yönlendirme).
Claim'leri ve süreyi test eder. Süresi dolmuş token'ın hâlâ kabul edilip edilmediğini; başka bir aud/iss için token'ın geçip geçmediğini; iptal edilen (logout) bir token'ın hâlâ geçerli olup olmadığını yoklar.
Savunmacı dersi: Saldırgan token'ın her parçasını (başlık, payload, imza) kurcalar ve kütüphanenin algoritmayı başlıktan seçip seçmediğini birincil olarak test eder. Savunma, algoritmayı sabitleyip her iddiayı doğrulamalıdır.
7. Exploit Mantığı
Bu bölüm çalıştırılabilir token forge örneği içermez. JWT saldırılarının neden bu kadar etkili olduğu kavramsaldır:
- İmza = kimlik. Geçerli imzalı bir token, sunucu için tartışmasız kimlik kanıtıdır. Saldırgan imzayı atlatabilir/üretebilirse, iddiaları serbestçe belirler → herhangi bir kullanıcıyı taklit, admin'e yükselme. Bu, kimlik doğrulamanın tümüyle çökmesidir.
- Açık anahtarın "bilinirliği" silaha döner. Algoritma karışıklığında, asimetrik güvenliğin temeli (açık anahtarın herkesçe bilinmesi) tersine döner: bilinen açık anahtar, HMAC sırrı olarak kullanılınca saldırganın elinde bir imzalama anahtarı olur. Bu, "asimetrik daha güvenli" sezgisinin nasıl tuzağa dönüştüğünü gösterir.
- Durumsuzluk iptali zorlaştırır. JWT sunucuda saklanmadığından, çalınan/forge edilen bir token
exp'e kadar geçerlidir; klasik "oturumu sonlandır" (Bölüm 16) doğrudan işlemez. Bu, kısa süre + iptal listesi gerektirir. - Sessizlik. Geçerli imzalı bir istek meşru görünür; forge edilmiş token klasik alarmları tetiklemez.
Araştırmacının çerçevesi: (a) algoritma token'dan mı seçiliyor (none/confusion), (b) sır güçlü mü, (c) claim'ler (exp/iss/aud) doğrulanıyor mu, (d) kid güvenli mi, (e) iptal var mı. Savunma (a)'yı algoritma sabitleyerek kapatır — en kritik hamle budur.
8. Güvenli Kod
Algoritmayı sabitleyen, sırrı güçlü, claim'leri doğrulayan sürüm (firebase/php-jwt):
<?php
// ✅ GÜVENLİ — algoritma SABİT, claim doğrulama, güçlü sır
declare(strict_types=1);
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
// 1) HMAC için: güçlü sır (CSPRNG, ≥256 bit — Böl. 13, 25)
$secret = getenv('JWT_SECRET'); // sır yöneticisinden (Böl. 25), kodda değil
// 2) Algoritmayı AÇIKÇA belirt — başlıktaki alg DİKKATE ALINMAZ
// (none + algoritma karışıklığı burada imkânsızlaşır)
try {
$decoded = JWT::decode($jwt, new Key($secret, 'HS256')); // algoritma sabit
} catch (\Firebase\JWT\ExpiredException $e) {
http_response_code(401); exit('Token süresi doldu'); // exp otomatik kontrol
} catch (\Throwable $e) {
http_response_code(401); exit('Geçersiz token');
}
// 3) Claim doğrulama: iss/aud (exp/nbf php-jwt tarafından kontrol edilir)
if (($decoded->iss ?? '') !== 'https://auth.example.com'
|| ($decoded->aud ?? '') !== 'https://api.example.com') {
http_response_code(401); exit('Geçersiz iss/aud');
}
$userId = $decoded->sub;
// --- Asimetrik (RS256) kullanılıyorsa: doğrulamada YALNIZCA açık anahtar + RS256 sabit ---
// $decoded = JWT::decode($jwt, new Key($publicKeyPem, 'RS256'));
// → HMAC yolu hiç devreye girmez; confusion imkânsız
// 4) Token üretme (HMAC)
$now = time();
$token = JWT::encode([
'iss' => 'https://auth.example.com',
'aud' => 'https://api.example.com',
'sub' => $userId,
'iat' => $now,
'nbf' => $now,
'exp' => $now + 900, // KISA süre (15 dk) — çalıntı/forge penceresini daralt
// rol/yetki sunucuda belirlenir; payload'a SIR koyma (şifreli değil!)
], $secret, 'HS256');
İptal ve yenileme (durumsuzluğun bedeli):
- Kısa erişim token süresi (dakikalar) + uzun ömürlü REFRESH token (sunucuda saklanan/iptal edilebilir)
- Logout / yüksek-değerli işlem: token'ı bir iptal (deny) listesine ekle veya anahtar/sürüm döndür
- jti (token id) + iptal listesi ile tekil iptal
Öne çıkan modern kalıplar:
- Algoritmayı açıkça belirt (en kritik): new Key($key, 'HS256') / 'RS256'. Başlıktaki alg'a asla güvenme. alg:none ve algoritma karışıklığını tek hamlede kapatır.
- HMAC ve RSA yollarını ayır: Doğrulama koduna hem HMAC hem RSA'yı aynı anahtar girişiyle verme; algoritma ve anahtar tipi birebir eşleşsin.
- Güçlü sır (Bölüm 13, 25): HS256 için CSPRNG ≥256 bit; kodda değil, sır yöneticisinde.
- Claim doğrulama: exp, nbf (kütüphane), iss, aud (elle) — token confusion ve süre atlatmayı kapatır.
- Payload'a sır koyma: Base64 şifreleme değildir; hassas veri JWT'de taşınmaz.
- Kısa süre + refresh + iptal: Durumsuzluğun iptal zorluğunu telafi et.
- kid'i güvenilmez say: Anahtar aramada parametreli sorgu/basename/beyaz liste (Bölüm 6, 20).
- Kütüphaneyi güncel tut (Bölüm 30): JWT açıklarının çoğu kütüphane sürümüne bağlıdır.
- JWT gerçekten gerekli mi? Tek bir sunucu/oturum için durumlu oturum (Bölüm 16) çoğu zaman daha basit ve iptal edilebilirdir.
9. Patch Analizi
Neden işe yarar? Algoritmayı açıkça belirtmek, saldırgan-kontrollü alg başlığını devre dışı bırakır: kütüphane yalnızca beklenen algoritmayı uygular, none'ı reddeder ve HMAC/RSA karışıklığını imkânsız kılar (RS256 beklenen kodda HMAC yolu hiç çalışmaz). Güçlü sır, çevrimdışı kaba kuvveti (Bölüm 13) ekonomik olmaktan çıkarır. Claim doğrulama, süresi dolmuş/yanlış hedefli token'ları reddeder. Bunlar birlikte, "imza her şeydir" modelini gerçekten güvenli kılar.
Saldırı-savunma haritası:
| Saldırı | Onu kapatan savunma |
|---|---|
alg:none |
Açık algoritma beyaz listesi |
| Algoritma karışıklığı (RS→HS) | Açık algoritma + HMAC/RSA yol ayrımı |
| Zayıf sır kaba kuvvet | CSPRNG ≥256 bit sır (Böl. 13, 25) |
| Süre/hedef atlatma | exp/nbf/iss/aud doğrulama |
kid enjeksiyonu |
Parametreli arama / beyaz liste (Böl. 6, 20) |
| Çalıntı token kalıcılığı | Kısa süre + refresh + iptal listesi |
| Payload sır ifşası | Payload'a sır koymama (şifreli değil) |
Durumsuzluk dengesi: JWT'nin yatay ölçeklenme avantajı, iptal zorluğuyla gelir. Yüksek-değerli oturumlar için kısa süre + sunucu-tarafı refresh/iptal, bu dengeyi güvenli kurar. Eğer iptal esnekliği kritikse, durumlu oturum (Bölüm 16) daha uygun olabilir — "JWT her yerde" bir zorunluluk değildir.
Performans: HMAC doğrulama çok hızlıdır; RSA/ECDSA biraz daha maliyetli ama ihmal edilebilir. İptal listesi kontrolü küçük bir arama ekler (durumsuzluğu kısmen azaltır ama güvenlik için gerekli olabilir).
10. Gerçek Hayat Senaryosu
Kurgu — bir mikroservis platformu. Kimlik servisi RS256 ile JWT üretiyor; diğer servisler açık anahtarla doğruluyor. Ama bir servis, doğrulama kütüphanesini algoritmayı token başlığından seçecek şekilde çağırıyor. Bir saldırgan, açık anahtarı /.well-known/jwks.json'dan alıp başlığı HS256 yaparak o açık anahtarla token imzalıyor (algoritma karışıklığı); sub'ı bir yöneticininki, rolü admin yapıyor. Doğrulama geçiyor ve saldırgan tüm platformda yönetici oluyor — özel anahtara hiç dokunmadan. Dersler: (1) algoritma daima uygulamada sabitlenmeli; (2) asimetrik kullanımda açık anahtarın "bilinir" olması, algoritma sabitlenmezse silaha döner; (3) tek bir yanlış doğrulama çağrısı, tüm mikroservis güvenini çökertir.
11. Gerçek CVE Analizi — JWT Kütüphane Açıkları (Tim McLean, 2015; CVE-2015-9235)
Auth0/Tim McLean açıklaması, MITRE/NVD (CVE-2015-9235) ve sonraki JWT CVE'leriyle doğrulanmıştır. JWT saldırı sınıflarını tanımlayan temel bulgudur.
Özet. 2015'te güvenlik araştırmacısı Tim McLean, çok sayıda dildeki popüler JWT kütüphanesinde — Node.js jsonwebtoken, Python pyjwt, namshi/jose, PHP php-jwt ve jsjwt — imza doğrulamasını atlatan iki temel kusur açıkladı. Node.js jsonwebtoken için bu CVE-2015-9235 olarak izlendi. Kütüphaneler haftalar içinde yamalandı; ama aynı iki hata sınıfı bugün hâlâ yeni kodda görülüyor.
Teknik neden — iki imza atlatma sınıfı.
1. alg:none atlatması. JWT standardı, bütünlüğü başka yolla doğrulanmış token'lar için bir none algoritması (imzasız) tanımlar. Etkilenen kütüphaneler, {"alg":"none"} başlıklı ve boş imzalı bir token'ı "geçerli, imzası doğrulanmış" sayıyordu. Sonuç: herkes, istediği payload'la kendi "imzalı" token'ını üretebiliyordu — imza gerekmeden. Bir sistemde bu, keyfi hesap erişimi/yetki yükseltmesi demekti. (Modern kütüphaneler artık bir anahtar verilmişse none'ı reddeder.)
2. Algoritma karışıklığı (RS→HS). Hem HMAC hem RSA destekleyen kütüphaneler, doğrulama algoritmasını token başlığından alıyordu. Bir sunucu RS256 kullanıyor ve doğrulamaya RSA açık anahtarını veriyorsa, saldırgan başlığı HS256 yapıp açık anahtarı HMAC sırrı olarak kullanarak token imzalıyordu. Açık anahtar tanım gereği bilinen bir değer olduğundan (HS256'nın güvenliği ise sırrın gizli olmasına dayandığından), saldırgan geçerli bir imza üretiyor ve özel anahtara hiç ihtiyaç duymadan doğrulamayı geçiyordu.
McLean'in vurguladığı doğru zihinsel model şudur: JWT kütüphanesine, beklediği algoritma söylenmeli; onu token'dan tespit etmesi istenmemelidir. Algoritmayı token'dan seçen her kod yolu, potansiyel bir algoritma karışıklığı açığıdır. Bu ilke, her iki sınıfı da tek hamlede kapatır.
Süregelen sınıf. Aynı kök neden sonraki yıllarda tekrar tekrar CVE üretti: PyJWT (CVE-2022-29217), json-web-token (CVE-2023-48238), jjwt (CVE-2018-1000531) ve Java ECDSA "Psychic Signatures" (CVE-2022-21449 — doğrulayıcının (0,0) imzasını geçerli kabul etmesi). Bu, tek bir yama değil, kalıcı bir tasarım tuzağı olduğunu gösterir.
Etki. İmza doğrulamasının atlatılması = kimlik doğrulamanın tümüyle çökmesi; keyfi kullanıcı taklidi ve yetki yükseltme. Etkilenen kütüphaneler çok yaygın olduğundan, sayısız uygulama savunmasızdı.
Çıkarılacak dersler:
1. Algoritmayı token'dan okuma; açıkça beyaz listele. alg:none ve algoritma karışıklığının tek ve kesin savunması budur.
2. Asimetrik ≠ otomatik güvenli. Açık anahtarın bilinirliği, algoritma sabitlenmezse HMAC karışıklığında silaha döner.
3. Kütüphaneyi güncel tut (Bölüm 30). JWT açıklarının çoğu sürüm-bağımlıdır; SCA ile bilinen-savunmasız sürümler yakalanır.
12. Detection
Kod incelemede: Algoritmanın nasıl seçildiğini ve claim doğrulamayı arayın:
grep -rnE 'JWT::decode|jwt_decode|verify' src/ # doğrulama çağrıları
grep -rn "header\['alg'\]\|\.alg\b" src/ # alg başlıktan mı okunuyor?
grep -rn "'none'\|alg.*none" src/ # none işleniyor mu?
grep -rnE "new Key\(" src/ | grep -v ',\s*'"'" # algoritma açıkça verilmiş mi?
grep -rn "kid" src/ | grep -iE 'query|include|exec' # kid injection
Kontroller: algoritma sabit mi (beyaz liste)? none reddediliyor mu? Sır güçlü ve kod-dışı mı? exp/iss/aud doğrulanıyor mu? kid güvenli mi?
SAST: Algoritmayı token'dan seçen doğrulama çağrılarını, none kabulünü ve kid enjeksiyon sink'lerini işaretler (taint-flow).
SCA (Bölüm 30): Bilinen-savunmasız JWT kütüphane sürümlerini (CVE-2015-9235 vb.) yakalar — uygulama kodu doğru olsa bile eski sürüm risklidir.
DAST: Kurcalanmış token'ları çalışan uygulamaya gönderir: {"alg":"none"}, açık anahtarla HS256 imzalı token, farklı aud iddialı token.
Loglar/SIEM: alg:none/beklenmeyen algoritmalı token denemeleri; imza doğrulama hatalarındaki artış; süresi dolmuş token kullanımı; ve forge başarılıysa — aynı sub için olağandışı yetki/erişim. JWT doğrulama hataları için özel loglama değerlidir.
13. Prevention
- Algoritmayı açıkça beyaz listele (en kritik): Doğrulamada algoritma uygulamada sabit;
algbaşlığına güvenme.nonereddedilir. - HMAC/RSA yollarını ayır: Aynı anahtar girişiyle her iki algoritmayı verme; tip birebir eşleşsin.
- Güçlü sır (Bölüm 13, 25): HS256 için CSPRNG ≥256 bit; sır yöneticisinde.
- Claim doğrula:
exp,nbf,iss,audher doğrulamada. - Payload'a sır koyma: JWT şifreli değildir.
- Kısa süre + refresh + iptal: Çalıntı/forge penceresini daralt; logout/yüksek-değerde iptal.
kid'i güvenilmez say (Bölüm 6, 20): Parametreli arama/beyaz liste.- Kütüphaneyi güncel tut + SCA (Bölüm 30).
- JWT gerekli mi değerlendir: Durumlu oturum (Bölüm 16) çoğu zaman daha basit ve iptal edilebilir.
14. Mitigation
- Acil: Doğrulamada algoritmayı sabitle;
none'ı reddet; zayıf sırrı güçlü CSPRNG sırla değiştir ve anahtarı döndür (tüm token'lar geçersiz olur);exp/iss/auddoğrulaması ekle. - Geçici: Forge/atlatma olduysa etkilenen hesapları belirle; imzalama anahtarını/sürümünü döndürerek tüm mevcut token'ları geçersiz kıl; kütüphaneyi güncelle (SCA).
- Kalıcı: Güvenli doğrulama kalıbını (açık algoritma + claim doğrulama) standartlaştır; kısa süre + iptal ekle; SAST/SCA + DAST token testleri; sırları sır yöneticisine al.
15. Checklist
- [ ] Doğrulamada algoritma açıkça beyaz listeleniyor mu (başlıktan seçilmiyor)?
- [ ]
alg:nonereddediliyor mu? - [ ] HMAC ve RSA doğrulama yolları ayrı mı (algoritma karışıklığı imkânsız mı)?
- [ ] HS256 sırrı CSPRNG ≥256 bit ve kod-dışı (sır yöneticisi) mi (Bölüm 25)?
- [ ]
exp,nbf,iss,audher doğrulamada kontrol ediliyor mu? - [ ] Payload'da sır/hassas veri yok, değil mi (şifreli değil)?
- [ ] Erişim token'ı kısa ömürlü mü; refresh + iptal mekanizması var mı?
- [ ]
kidgüvenli mi (parametreli/beyaz liste — enjeksiyon yok)? - [ ] JWT kütüphanesi güncel ve SCA ile izleniyor mu (Bölüm 30)?
16. Laboratuvar
Lab 23.1 — alg:none. İzole ortamda algoritmayı başlıktan seçen bir doğrulayıcı kur; {"alg":"none"} + boş imzayla istediğin rolde token üret ve kabul edildiğini gözlemle. Açık algoritma beyaz listesiyle (new Key($key,'HS256')) kapat.
Lab 23.2 — Algoritma karışıklığı. RS256 beklenen bir doğrulayıcıya açık anahtarı ver; başlığı HS256 yapıp açık anahtarla imzalayarak atlatmayı göster (kendi test anahtarınla). Algoritmayı RS256'ya sabitleyip HMAC yolunu kapat.
Lab 23.3 — Zayıf sır. Zayıf bir HS256 sırrıyla token üret; çevrimdışı bir araçla (izole) sırrı kaba kuvvetle bul. Güçlü CSPRNG sırla farkı gözlemle.
Lab 23.4 — Claim doğrulama. exp/aud kontrol etmeyen bir doğrulayıcıya süresi dolmuş / farklı aud'lu token gönder; kabul edildiğini gör. Claim doğrulaması ekleyip düzelt.
17. Quiz
- JWT'nin üç bölümü nedir? Payload neden şifreli değildir ve bu ne anlama gelir?
- Simetrik (HS256) ve asimetrik (RS256) imzalama arasındaki fark nedir? Hangi anahtar neyi yapar?
alg:nonesaldırısı nasıl çalışır? Kütüphaneler bunu nasıl önler?- Algoritma karışıklığı (RS256→HS256) nasıl çalışır? Açık anahtarın "bilinir" olması neden silaha döner?
- Tüm JWT imza atlatmalarının ortak kök nedeni nedir? Tek en önemli savunma nedir?
- "Kütüphaneye algoritmayı söyle, tespit etmesini isteme" ilkesi ne demektir?
- Zayıf HMAC sırrı neden tehlikelidir (Bölüm 13'e atıfla)?
- Hangi claim'ler doğrulanmalı ve her biri neyi önler (
exp,iss,aud)? kidenjeksiyonu nedir ve hangi açık sınıflarına (Bölüm 6, 20, 8) bağlanır?- JWT'nin durumsuzluğu iptal için neden bir zorluktur? Nasıl telafi edilir?
- Tim McLean 2015 bulgusunun iki sınıfı nelerdi ve neden hâlâ görülüyorlar?
- Ne zaman durumlu oturum (Bölüm 16) JWT'den daha uygun olabilir?
18. Kaynakça
- OWASP, JSON Web Token for Java/General Cheat Sheet; Testing JSON Web Tokens (WSTG).
- Tim McLean (Auth0), Critical vulnerabilities in JSON Web Token libraries (2015).
- MITRE / NVD, CVE-2015-9235 (jsonwebtoken); ayrıca CVE-2022-29217 (PyJWT), CVE-2023-48238, CVE-2018-1000531, CVE-2022-21449 (ECDSA "Psychic Signatures").
- IETF RFC 7519 (JWT), RFC 7515 (JWS), RFC 8725 (JSON Web Token Best Current Practices).
- firebase/php-jwt dokümantasyonu (
JWT::decode,Key, açık algoritma kullanımı). - PortSwigger Web Security Academy, JWT attacks (alg:none, algorithm confusion, kid injection).