# JWT Authentication

> JWT (JSON Web Token) ile kimlik doğrulama: token yapısı, çalışma mantığı, imzalama ve doğrulama, bedeli ve yıllar içinde gördüğüm tipik hatalar.

- Yayın: 2024-07-13
- Güncelleme: 2026-10-07
- Kategori: Web Geliştirme
- Etiketler: JWT
- Okuma süresi: 3 dk okuma
- Kaynak: https://muhammetsafak.com/tr/blog/jwt-authentication/
- Dil: tr-TR
- Yazar: Muhammet Şafak

---
> **Kısaca:** JWT, header.payload.signature biçiminde, imzalı ve kompakt bir token'dır; sunucu oturum durumu tutmadan isteği doğrulayabilir. Bedeli, süresi dolmadan geri çağrılamamasıdır; bu yüzden en çok kısa ömürlü erişime uyar.

JWT, modern web uygulamalarında kimlik doğrulamanın varsayılan aracı haline geldi. Bu yaygınlık iyi bir şey; ama "varsayılan" olması, her durumda doğru seçim olduğu anlamına gelmiyor. Kimlik doğrulamanın en yalın hâlini [HTTP Basic Authentication yazısında](/tr/blog/basic-authentication/) anlatmıştım; JWT'nin ne getirdiğini görmek için önce onun neyi getirmediğine bakmak işe yarıyor. Bu yazıda JWT'nin nasıl çalıştığını, ne zaman işe yaradığını ve yıllar içinde gördüğüm tipik hataları aktarıyorum.

## JWT nedir ve neyi çözer?

JWT (JSON Web Token), iki taraf arasında doğrulanabilir biçimde bilgi taşıyan bir token formatıdır; [RFC 7519: JSON Web Token (JWT), May 2015](https://www.rfc-editor.org/rfc/rfc7519) onu "a compact, URL-safe means of representing claims to be transferred between two parties" diye tanımlar. Nokta ile ayrılmış üç bölümden oluşur:

```text
header.payload.signature
```

`header` token türünü ve imzalama algoritmasını söyler; `payload` taşınan veriyi tutar; `signature` ise ilk iki bölümün gizli bir anahtarla imzalanmış halidir. İmza sayesinde sunucu, token içeriğinin değiştirilmediğini — anahtarı token'ın içinde taşımadan — doğrulayabilir.

JWT'nin çözdüğü asıl problem şu: sunucu, oturum bilgisini kendi tarafında saklamak zorunda kalmaz. Token kendi kendini doğrular. Bu, birden çok sunucuya yayılmış bir sistemde oturumu paylaşma derdini ortadan kaldırır.

## Bedeli ne

Bu durumsuzluk bedava değil. Stateful bir oturumda kullanıcıyı anında atabilirsiniz — kaydı silersiniz, biter. JWT'de token, siz ne yaparsanız yapın, süresi dolana kadar geçerlidir. Ele geçirilmiş bir token'ı geri çağırmak, JWT'nin doğası gereği zordur.

Bu yüzden JWT'yi şöyle konumlandırıyorum: kısa ömürlü erişim için iyi; "oturumu her an anında iptal edebilmem gerek" diyen senaryolar için zayıf.

Bu zayıflığı bir kara liste (blacklist) ile yamayabilirsiniz — iptal edilen token'ları bir yerde tutup her istekte kontrol edersiniz. Ama dikkat edin: o an JWT'nin baştaki vaadini, yani "sunucu hiçbir oturum durumu tutmasın" ilkesini terk etmiş olursunuz. Kara liste eklediğiniz noktada, durumsuzluğun getirdiği kolaylığın bir kısmını geri vermiş olursunuz. Bu kötü bir karar değil; ama bilinçli verilmesi gereken bir karar.

## Pratikte: imzalama ve doğrulama

Konuyu somutlaştırmak için imzalama ve doğrulamayı elle yapan küçük bir sınıf. Production'da bunu kendiniz yazmayın — `firebase/php-jwt` gibi denenmiş bir paket kullanın; aşağıdaki yalnızca mekanizmayı göstermek için:

```php
class JWT
{
    protected const ALGORITHM = 'HS256';
    protected const SIGNATURE_ALGORITHM = 'sha256';

    public static function encode(string $key, array $payload, ?int $ttl = null): string
    {
        $header = base64_encode(json_encode([
            'typ' => 'JWT',
            'alg' => self::ALGORITHM,
        ]));
        $payload = base64_encode(json_encode([
            'data'       => $payload,
            'created_at' => time(),
            'ttl'        => $ttl,
        ]));

        return $header . '.' . $payload . '.' . self::signature($key, $header, $payload);
    }

    public static function decode(string $key, string $jwt): array|false
    {
        try {
            [$h, $p, $signature] = explode('.', $jwt);
            if (!hash_equals(self::signature($key, $h, $p), $signature)) {
                return false;
            }
            $payload = json_decode(base64_decode($p), true);
            if ($payload['ttl'] !== null && time() - $payload['created_at'] > $payload['ttl']) {
                return false;
            }
            return $payload['data'];
        } catch (\Throwable) {
            return false;
        }
    }

    private static function signature(string $key, string $header, string $payload): string
    {
        return base64_encode(hash_hmac(self::SIGNATURE_ALGORITHM, $header . '.' . $payload, $key, true));
    }
}
```

Sınıfın kendisi için bir not: `base64_encode()` kullanıyor, oysa JWS belirtimi base64url ister; [RFC 7515: JSON Web Signature (JWS), May 2015](https://www.rfc-editor.org/rfc/rfc7515) bunu URL- ve dosya adı-güvenli karakter kümesiyle yapılan, sondaki tüm `=` karakterleri atılmış Base64 olarak tanımlar. `data`, `created_at` ve `ttl` alanları da RFC 7519'un kayıtlı claim'leri değil, bana ait alanlardır; bu sınıfın ürettiği token'lar standart JWT kütüphaneleriyle uyumlu çalışmaz.

Burada gözden kaçmaması gereken bir satır var: imza karşılaştırmasını `hash_equals()` ile yapıyorum, `==` ile değil. Sıradan karşılaştırma, yanıt süresinden bilgi sızdıran zamanlama saldırılarına (timing attack) açıktır. Küçük bir detay; ama güvenlik kodunda "küçük detay" diye bir şey yok.

## Yıllar içinde gördüğüm hatalar

- **Uzun ömürlü token.** Erişim token'ı 15 dakika–1 saat arası olmalı; kalıcı oturum için ayrı, daha uzun ömürlü bir refresh token kullanın.
- **Yanlış yerde saklamak.** Token'ı `localStorage`'a koymak onu XSS'e açar. `HttpOnly` ve `Secure` bir cookie daha güvenlidir.
- **Payload'a hassas veri koymak.** `payload` imzalıdır ama şifreli değildir — Base64 çözen herkes okuyabilir. Parola, kimlik numarası gibi şeyler oraya yazılmaz.
- **`alg: none` ihtimalini unutmak.** Doğrulama tarafı algoritmayı token'ın kendisinden körü körüne okursa, saldırgan imzayı tamamen atlayabilir. Beklediğiniz algoritmayı kodda sabitleyin, token'a güvenmeyin. [RFC 8725: JSON Web Token Best Current Practices, February 2020](https://www.rfc-editor.org/rfc/rfc8725) de aynısını söyler: kütüphaneler "MUST enable the caller to specify a supported set of algorithms and MUST NOT use any other algorithms when performing cryptographic operations".

## Özet

JWT zarif bir mekanizma; ama sihir değil. Durumsuzluğun getirdiği ölçeklenme kolaylığını, iptal etme zorluğu olarak geri ödüyorsunuz. Token'ları kısa tutarsanız, doğru yerde saklarsanız ve imzayı titizce doğrularsanız, bu takas çoğu uygulama için makul. Bu koşulları sağlayamıyorsanız, klasik sunucu oturumu hâlâ saygın bir seçenektir — yeni olan, her zaman daha iyi anlamına gelmiyor.
