Selam TurkTeam, BlueTornado burada.
JWT (JSON Web Token), iddia (claim) taşımak için yaygın bir formattır; "otomatik olarak güvenli oturum çözümü" değildir. Yanlış kullanıldığında alg:none, zayıf imza, uzun ömürlü token, secret sızıntısı gibi hatalar ortaya çıkar. Bu rehberde JWT yapısı, klasik oturum çerezi ile farkı, yaygın hatalar ve savunmacı doğru kullanım anlatılır.
Konuda Anlatılacaklar
- JWT nedir, ne işe yarar?
- Oturum çerezi ile karşılaştırma
- Lab: token üretme ve inceleme
- Yaygın hatalar (alg, secret, saklama)
- Doğru kullanım: imza, süre, yenileme, iptal
- İleri seviye: JWE, key rotation, browser saklama
- Kaynakça ve yasal uyarı
1) JWT Nedir?
JWT genellikle üç Base64URL parçasından oluşur: header.payload.signature
# Örnek yapı (sahte / eğitim)
header = {"alg":"HS256","typ":"JWT"}
payload = {"sub":"123","role":"user","exp":1735689600}- Header: algoritma ve tip
- Payload: claim'ler (sub, exp, iss, aud, özel alanlar)
- Signature: bütünlük; gizli anahtar veya özel anahtar ile
Payload varsayılan olarak şifreli değildir; Base64 decode herkes yapar. Gizlilik için TLS şarttır; ek gizlilik gerekirse JWE düşünülür.
Alıntı:JWT'yi "encrypt" sanmak yaygın hatadır. İmza bütünlük sağlar; içeriği gizlemez.
2) Klasik Oturum ile Fark
- Sunucu oturumu: Çerezde rastgele session id; veri sunucuda. İptal kolay (store'dan sil).
- Stateless JWT: Veri token içinde; ölçeklenmesi kolay, iptal zor (blacklist/ kısa ömür gerekir).
İkisi de doğru tasarlanırsa güvenli olabilir. Çoğu web uygulaması için HttpOnly Secure SameSite çerez + sunucu oturumu hâlâ sağlam bir seçimdir. JWT özellikle API'ler, mikroservisler ve mobil istemcilerde tercih edilir.
3) Lab: Token İnceleme
# Python ile imzalı token (lab secret - üretime koymayın)
pip install pyjwt
python3 - <<'PY'
import jwt, datetime
key = "lab-only-change-me"
payload = {
"sub": "42",
"role": "user",
"exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=15),
"iat": datetime.datetime.utcnow(),
"iss": "turkteam-lab",
}
token = jwt.encode(payload, key, algorithm="HS256")
print(token)
print(jwt.decode(token, key, algorithms=["HS256"], issuer="turkteam-lab"))
PY# Parçaları görmek (imza doğrulaması değil!)
python3 - <<'PY'
import base64, json
t = input().strip().split('.')
for part in t[:2]:
pad = '=' * (-len(part) % 4)
print(json.loads(base64.urlsafe_b64decode(part + pad)))
PY[center][/center]
4) Yaygın Hatalar
alg:none veya algoritma karışıklığı:
# Kütüphane "alg" header'ına körü körüne güvenmemeli
# Doğrulamada algoritmayı allowlist ile sabitleyin
jwt.decode(token, key, algorithms=["HS256"]) # algorithms zorunlualgZayıf / sızmış secret:
# Kötü
secret = "secret"
# İyi: uzun, rastgele, ortam değişkeni / KMS
# openssl rand -base64 48exp yok veya çok uzun: Yıllarca geçerli access token çalınırsa uzun süre geçerlidir.
Hassas veri payload'da: TCKN, parola hash'i, kart numarası koymayın.
localStorage'da access token: XSS ile çalınır. Mümkünse Secure HttpOnly çerez; XSS'i de kapatın.
Imza doğrulamadan claim okumak:
# YANLIŞ
payload = jwt.decode(token, options={"verify_signature": False})[center][/center]
5) Doğru Kullanım Pratikleri
- Access token kısa ömürlü (dakikalar); refresh token uzun, sıkı saklı, rotate edilebilir
- ,Kod:
iss,Kod:aud,Kod:expdoğrulayınKod:nbf - HS256 için güçlü secret; asimetrik için RS256/ES256 ve JWKS
- Rol/yetki değişince eski token'lara güvenmeyin (kısa TTL veya sürüm claim'i)
- Logout: refresh iptali + çerez silme; saf stateless'ta kısa TTL şart
- TLS zorunlu; token'ı URL query'de taşımayın (loglara düşer)
# Doğrulama örneği
jwt.decode(
token,
key,
algorithms=["HS256"],
issuer="turkteam-lab",
audience="turkteam-api",
options={"require": ["exp", "iss", "aud", "sub"]},
)# Refresh rotate (kavramsal)
# Her kullanımda yeni refresh üret, eskisini revoke et
# Çalınan refresh replay edilirse aile (family) iptali6) Oturum Çerezi Sertleştirme (JWT kullanmasanız bile)
Set-Cookie: session=...; Path=/; Secure; HttpOnly; SameSite=Lax; Priority=High- Session fixation: login sonrası id yenile
- Idle / absolute timeout
- Concurrent session politikası (isteğe bağlı)
7) İleri Seviye
JWKS ve key rotation:
kidGET /.well-known/jwks.jsonJWE: Token içeriğini şifrelemek gerektiğinde. Karmaşıklık artar; çoğu API için TLS + minimal claim yeterlidir.
Service-to-service: Kullanıcı JWT'sini körü körüne iletmeyin; audience daraltın, impersonation claim'lerini dikkatli tasarlayın.
# Rapor dili
Bulgu: JWT imza doğrulamasında algoritma sabitlenmemiş
Risk: Algorithm confusion / alg none sınıfı hatalara açıklık
Öneri: algorithms allowlist + güçlü anahtar + kısa exp[center][/center]
8) Sonuç
JWT güçlü bir formattır; güvenlik tasarımında detaylarda gizlidir. İmza algoritmasını sabitleyin, süreyi kısa tutun, saklamayı XSS'e karşı düşünün, claim'leri doğrulayın. Gerekmiyorsa sunucu oturumu ile de ilerleyebilirsiniz.
Ek: Tarayıcı Saklama Karar Ağacı
- Birinci taraf web uygulaması: mümkünse sunucu oturumu veya refresh'i HttpOnly çerezde tutun
- Access token bellekte (memory) kısa ömürlü tutulabilir; sayfa yenilenince refresh ile yenilenir
- localStorage/sessionStorage: XSS yüzeyi geniş; yalnızca risk kabulü ile
- Mobil uygulama: OS güvenli depolama (Keychain / Keystore)
# Nginx ile çerez örneği (uygulama Set-Cookie üretiyorsa)
# proxy_cookie_flags ~ secure httponly samesite=lax;Test kontrol listesi:
[ ] algorithms parametresi sabit mi?
[ ] exp / iss / aud doğrulanıyor mu?
[ ] Secret git'te yok mu?
[ ] Access TTL kısa mı?
[ ] Refresh rotate / revoke var mı?
[ ] Token URL'de taşınmıyor mu?
[ ] Logout sonrası refresh geçersiz mi?[center][/center]
Kaynakça
- RFC 7519 - JWT
- OWASP JWT Cheat Sheet
- OWASP Session Management Cheat Sheet
- JWT BCP tartışmaları / best practices (Auth0 özeti, RFC 8725 ile birlikte okuyun)
- RFC 8725 - JWT Best Current Practices
- PyJWT Documentation
Yasal Uyarı
Token üretme/kırma denemeleri yalnızca kendi labınızda veya yazılı yetki ile yapılmalıdır. Başkalarının oturum token'larını ele geçirmek veya kullanmak TCK 243-245 kapsamında suç olabilir. Eğitim amaçlıdır.
Yasal uyarı: Bu içerik eğitim amaçlıdır. Yalnızca kendi sistemlerinizde veya yazılı izin aldığınız hedeflerde uygulayın (TCK 243-245).