Yazılarımız

OfisData

JWT AUTHENTİCATİON KURMAK VE TOKEN GÜVENLİĞİNİ ARTIRMAK

Bir API’yi yayına almak kolay; onu güvenle ayakta tutmak ise ayrı bir iş. Kullanıcı girişinden sonra oturum yönetimini doğru kurgulamazsanız, en iyi özellikler bile tek bir çalınmış token ile risk altına girebilir.

JWT (JSON Web Token) bu noktada sık tercih edilen bir yaklaşım: stateless çalışır, mikro servislerde taşınması rahattır ve iyi tasarlanırsa ölçeklenebilir bir kimlik doğrulama deneyimi sağlar. Ancak “JWT kullanıyorum” demek tek başına güvenlik demek değildir.

Bu rehberde JWT authentication kurmak için sağlam bir temel oluşturacağız; ardından token güvenliğini artırmak için refresh token rotasyonu, cookie stratejileri, hata senaryoları ve pratik kontrol listesiyle devam edeceğiz. Eğitim ihtiyacınız varsa, ekipçe aynı standartta ilerlemek için Node.js eğitimi içeriğine de göz atabilirsiniz.

JWT authentication kurmak için temel akış tasarlamak

Primary yaklaşım: kullanıcı giriş yapar, sunucu bir access token üretir ve istemci bunu sonraki isteklerde taşır. Access token kısa ömürlü tutulur; uzun oturum ihtiyacı için ayrı bir refresh token kullanılır.

JWT içinde genellikle kullanıcı kimliği (sub), rol/izin bilgileri, token türü, üretim zamanı ve son kullanma (exp) gibi claim’ler bulunur. Burada kritik konu, token’a gereğinden fazla veri koymamak ve her claim’in kullanım amacını netleştirmektir.

Access token ve refresh token rollerini netleştirmek

Access token: API çağrılarında kimliği kanıtlamak için kullanılır, kısa ömürlüdür (ör. 10–15 dakika). Refresh token: yeni access token almak için kullanılır, daha uzun ömürlüdür (ör. 7–30 gün) ve daha sıkı korunmalıdır.

İmzalama algoritması ve anahtar yönetimini planlamak

HS256 (shared secret) daha basit; RS256/ES256 (public/private key) daha ölçeklenebilir yönetim sağlar. Ekip büyüdükçe anahtar döndürmeyi (key rotation) ve ortam değişkeni yönetimini baştan düşünmek işleri kolaylaştırır.

Bir Node.js API akışında erişim ve yenileme token yaşam döngüsünü anlatan şema ve kilit simgeleri

Token güvenliğini artırmak için saklama stratejileri seçmek

En çok hata yapılan yer, token’ın nerede saklandığıdır. Tarayıcı tarafında localStorage kullanımı, XSS riskinde token sızdırmayı kolaylaştırabilir. Bunun yerine refresh token’ı httpOnly cookie içinde tutmak yaygın bir güvenli seçenektir.

Access token için iki yaklaşım öne çıkar: (1) Bellekte tutup her sayfa yenilemede yenilemek, (2) Kısa ömürlü access token’ı da cookie’de taşımak. Seçim; uygulama mimarinize, XSS/CSRF risklerinize ve kullanıcı deneyimi beklentinize göre yapılmalı.

httpOnly cookie ile refresh token taşımayı kurgulamak

Refresh token cookie’deyse JS ile okunamaz; bu, XSS anında doğrudan token çalınmasını zorlaştırır. Cookie’ye Secure ve SameSite eklemek, taşıma güvenliğini ciddi şekilde güçlendirir.

CSRF riskini azaltmak için SameSite ve token çifti kullanmak

Cookie kullanımı CSRF riskini gündeme getirir. SameSite=Lax/Strict çoğu senaryoda koruma sağlar; daha hassas akışlarda CSRF token (double submit) yaklaşımı eklenebilir. Önemli olan, risk değerlendirmesi yapıp önlemi ölçülü uygulamaktır.

Node.js ve Express ile JWT üretmek ve doğrulamak

Uygulamada iki kritik uç nokta düşünün: /login ve /refresh. Login, access+refresh üretir; refresh ise geçerli refresh token ile yeni access üretir (gerekirse refresh’i de yeniler).

Giriş endpoint’inde access ve refresh token üretmek

Aşağıdaki örnek, kullanıcı doğrulandıktan sonra access token’ı JSON olarak döndürür, refresh token’ı ise httpOnly cookie’ye yazar. Gerçekte kullanıcı doğrulama kısmı (şifre karşılaştırma, rate limit, audit log) ayrıca ele alınmalıdır.

import express from "express";
import jwt from "jsonwebtoken";
import cookieParser from "cookie-parser";

const app = express();
app.use(express.json());
app.use(cookieParser());

const ACCESS_TOKEN_TTL = "15m";
const REFRESH_TOKEN_TTL = "14d";

const signAccessToken = (user) => {
  return jwt.sign(
    { sub: user.id, role: user.role, typ: "access" },
    process.env.JWT_ACCESS_SECRET,
    { expiresIn: ACCESS_TOKEN_TTL, issuer: "api", audience: "web" }
  );
};

const signRefreshToken = (user, tokenId) => {
  return jwt.sign(
    { sub: user.id, jti: tokenId, typ: "refresh" },
    process.env.JWT_REFRESH_SECRET,
    { expiresIn: REFRESH_TOKEN_TTL, issuer: "api", audience: "web" }
  );
};

app.post("/login", async (req, res) => {
  const { email, password } = req.body;

  // 1) Kullanıcıyı bul, şifreyi doğrula (örnek: bcrypt.compare)
  const user = { id: "u_123", role: "admin", email };

  // 2) Refresh token için benzersiz bir jti üret (DB'de saklamak için)
  const tokenId = crypto.randomUUID();

  const accessToken = signAccessToken(user);
  const refreshToken = signRefreshToken(user, tokenId);

  res.cookie("rt", refreshToken, {
    httpOnly: true,
    secure: true,
    sameSite: "lax",
    path: "/",
    maxAge: 14 * 24 * 60 * 60 * 1000
  });

  return res.json({ accessToken });
});

Yetkilendirme kontrolünü sağlamlaştırmak ve hataları yönetmek

Kimlik doğrulama ile yetkilendirme farklıdır: token geçerli olabilir, ama kullanıcı o kaynağa erişemez. Bu yüzden API katmanında rol/izin kontrolünü net bir yapıyla kurgulamak gerekir.

JWT doğrulama middleware’i ile istekleri güvenle süzmek

Access token doğrulamasını tek bir middleware’de toplamak, hem standartlaşmayı sağlar hem de hata yönetimini sadeleştirir. Dikkat edilmesi gereken nokta: hata mesajlarında fazla bilgi vermemek ve log’larda iz bırakmaktır.

import jwt from "jsonwebtoken";

export function requireAuth(req, res, next) {
  const header = req.headers.authorization || "";
  const token = header.startsWith("Bearer ") ? header.slice(7) : null;

  if (!token) return res.status(401).json({ message: "Giriş gerekli." });

  try {
    const payload = jwt.verify(token, process.env.JWT_ACCESS_SECRET, {
      issuer: "api",
      audience: "web"
    });

    if (payload.typ !== "access") {
      return res.status(401).json({ message: "Token türü geçersiz." });
    }

    req.user = { id: payload.sub, role: payload.role };
    return next();
  } catch (err) {
    return res.status(401).json({ message: "Oturum süresi dolmuş olabilir." });
  }
}

export function requireRole(roles = []) {
  return (req, res, next) => {
    if (!req.user) return res.status(401).json({ message: "Giriş gerekli." });
    if (!roles.includes(req.user.role)) return res.status(403).json({ message: "Yetki yok." });
    return next();
  };
}

Refresh token rotasyonu ile sızıntı etkisini azaltmak

Uzun ömürlü refresh token çalınırsa, saldırgan sürekli yeni access token alabilir. Bunu azaltmanın güçlü yollarından biri refresh token rotasyonudur: her yenilemede yeni refresh üretir, eskiyi iptal edersiniz.

Rotasyonun kalbi, refresh token’ın içindeki jti (tokenId) ve sunucuda tuttuğunuz kayıt (DB veya cache) arasındaki bağdır. Yeni jti üretip eskisini geçersiz yaparak tekrar kullanım (replay) saldırılarını yakalayabilirsiniz.

Token rotasyonunda tekrar kullanım tespiti ve oturum kapatmak

Eski refresh token yeniden kullanılıyorsa, bu genellikle sızıntı işaretidir. Bu durumda yalnızca o token’ı değil, ilgili kullanıcıya ait aktif refresh kayıtlarını da iptal etmek daha güvenli olabilir. Böylece saldırganın “eski token” ile ilerlemesi zorlaşır.

Bir ekip toplantısında API erişim günlükleri ve güvenlik uyarılarıyla risk analizi yapılması

Pratik güvenlik kontrol listesiyle yayına hazırlamak

JWT uygulaması, tek bir kütüphane seçimiyle bitmez; yapı taşları bir araya gelince anlam kazanır. Aşağıdaki liste, yayına çıkmadan önce hızlı ama etkili bir denetim sağlar.

Yayına çıkmadan önce kritik ayarları doğrulamak

  • Access token süresini kısa tutmak (ör. 10–15 dk) ve yenilemeyi refresh ile yapmak
  • Refresh token’ı httpOnly + Secure cookie içinde taşımak, SameSite’ı risk profilinize göre seçmek
  • JWT secret/anahtarlarını düzenli döndürmek ve sızıntı halinde hızlı iptal planı yapmak
  • Issuer/audience doğrulamasıyla yanlış token kabulünü engellemek
  • Rate limiting, brute force koruması ve şüpheli denemeleri izlemek

Ek olarak, XSS yüzeyini azaltmak için içerik güvenliği politikaları (CSP), güvenli bağımlılık yönetimi ve kullanıcı girdisi temizliği önemlidir. Güvenliği tek katmanda değil, katmanlı biçimde düşünmek kazandırır.

Performans ve kullanıcı deneyimi dengesini korumak

Token süresi kısaldıkça güvenlik artar ama yenileme sıklığı yükselir. Bu dengeyi doğru kurmak için istemci tarafında sessiz yenileme, hata yakalama ve yeniden deneme stratejileri tasarlamak gerekir.

Örneğin access token süresi dolduğunda 401 dönünce otomatik /refresh çağırıp isteği tekrar etmek yaygın bir yaklaşımdır. Burada önemli olan, sonsuz döngüye girmemek ve yenileme başarısızsa kullanıcıyı güvenli şekilde yeniden girişe yönlendirmektir.

İstemci tarafında yenileme senaryolarını sadeleştirmek

İstemci tek bir “token yenileme” modülü kullanırsa, farklı sayfalarda farklı davranışlar oluşmaz. Ayrıca çoklu isteklerin aynı anda yenilemeye çalışması (race condition) kontrol altına alınır. Bu, hem hata oranını düşürür hem de destek yükünü azaltır.

Kod editöründe Node.js projesinde kimlik doğrulama middleware dosyasının düzenlenmesi ve test ekranı

Ekip içi standartlaştırma ile bakım maliyetini düşürmek

JWT uygulaması bir kez yazılıp bırakılan bir parça değildir; yeni servisler, yeni istemciler, yeni güvenlik gereksinimleriyle evrilir. Bu yüzden ekip içinde net standartlar belirlemek, bakım maliyetini azaltır.

Öneri: token claim sözleşmesi (sub, role, typ, jti), hata kodları, log formatı, anahtar yönetimi ve rotasyon kuralı gibi başlıkları kısa bir dokümana dökün. Geliştirici değişse de sistem aynı şekilde çalışsın.

Test senaryolarıyla güvenlik regresyonunu engellemek

Mutlaka otomatik testlerde; süresi dolmuş token, yanlış audience, yanlış issuer, bozuk imza, rol yetersizliği, refresh tekrar kullanımı ve cookie ayarları gibi durumları doğrulayın. Bu testler, küçük bir refactor’ın bile güvenliği bozmasını önler.

Özetle: JWT doğru kurulduğunda hem ölçeklenebilir hem de pratik bir kimlik doğrulama çözümüdür. “Token üretmek” kadar, token’ın yaşam döngüsünü yönetmek ve saldırı yüzeyini azaltmak da önemlidir. Bu yaklaşımı ekipçe hızla oturtmak için Node.js eğitimi ile ortak bir standarda geçmek işinizi kolaylaştırır.

 Vimaj