Ana içeriğe geç

JWT Authentication

JWT (JSON Web Token), stateless kimlik doğrulama sağlar; yanlış yapılandırma güvenlik açıklarına ve token sızıntılarına yol açar.


1. Zayıf Secret Key Kullanmak

Yanlış Kullanım: Kısa ve tahmin edilebilir secret key kullanmak.

var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("mysecret")); // Çok kısa, brute-force'a açık

İdeal Kullanım: Yeterli uzunlukta ve güvenli secret key kullanın.

var secretKey = builder.Configuration["Jwt:Secret"]; // Minimum 256-bit (32 byte)
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey));

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = builder.Configuration["Jwt:Issuer"],
            ValidAudience = builder.Configuration["Jwt:Audience"],
            IssuerSigningKey = key,
            ClockSkew = TimeSpan.Zero
        };
    });

2. Token’da Hassas Veri Taşımak

Yanlış Kullanım: JWT payload’una şifre veya kişisel veri koymak.

var claims = new[]
{
    new Claim("email", user.Email),
    new Claim("password", user.Password),      // Şifre token'da!
    new Claim("creditCard", user.CreditCard),   // Kart bilgisi token'da!
    new Claim("address", user.Address)
};

İdeal Kullanım: Token’da sadece gerekli minimum bilgiyi taşıyın.

var claims = new[]
{
    new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()),
    new Claim(JwtRegisteredClaimNames.Email, user.Email),
    new Claim(ClaimTypes.Role, user.Role),
    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
};

3. Token Expiry Süresini Çok Uzun Tutmak

Yanlış Kullanım: Token’ı günlerce veya haftalarca geçerli tutmak.

var token = new JwtSecurityToken(
    expires: DateTime.UtcNow.AddDays(30), // 30 gün geçerli, çalınırsa uzun süre kötüye kullanılır
    // ...
);

İdeal Kullanım: Kısa ömürlü access token ve refresh token kullanın.

public class TokenService
{
    public TokenPair GenerateTokens(User user)
    {
        var accessToken = GenerateAccessToken(user, TimeSpan.FromMinutes(15));
        var refreshToken = GenerateRefreshToken();

        return new TokenPair(accessToken, refreshToken);
    }

    private string GenerateAccessToken(User user, TimeSpan expiry)
    {
        var token = new JwtSecurityToken(
            issuer: _config["Jwt:Issuer"],
            audience: _config["Jwt:Audience"],
            claims: GetClaims(user),
            expires: DateTime.UtcNow.Add(expiry),
            signingCredentials: _credentials);

        return new JwtSecurityTokenHandler().WriteToken(token);
    }

    private string GenerateRefreshToken()
    {
        var randomBytes = RandomNumberGenerator.GetBytes(64);
        return Convert.ToBase64String(randomBytes);
    }
}

4. Token Validation Parametrelerini Gevşek Bırakmak

Yanlış Kullanım: Validation parametrelerini devre dışı bırakmak.

options.TokenValidationParameters = new TokenValidationParameters
{
    ValidateIssuer = false,       // Issuer kontrol edilmiyor
    ValidateAudience = false,     // Audience kontrol edilmiyor
    ValidateLifetime = false,     // Süresi dolmuş tokenlar kabul ediliyor
    ValidateIssuerSigningKey = false // İmza doğrulanmıyor!
};

İdeal Kullanım: Tüm validation parametrelerini aktif tutun.

options.TokenValidationParameters = new TokenValidationParameters
{
    ValidateIssuer = true,
    ValidateAudience = true,
    ValidateLifetime = true,
    ValidateIssuerSigningKey = true,
    ValidIssuer = builder.Configuration["Jwt:Issuer"],
    ValidAudience = builder.Configuration["Jwt:Audience"],
    IssuerSigningKey = new SymmetricSecurityKey(
        Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Secret"])),
    ClockSkew = TimeSpan.Zero // Varsayılan 5 dakika toleransı kaldır
};

5. Token Revocation Mekanizması Olmamak

Yanlış Kullanım: Çıkış yapıldığında token’ı geçersiz kılamamak.

[HttpPost("logout")]
public IActionResult Logout()
{
    return Ok("Çıkış yapıldı");
    // Token hala geçerli! Süresi dolana kadar kullanılabilir
}

İdeal Kullanım: Token blacklist veya refresh token revocation uygulayın.

[HttpPost("logout")]
public async Task<IActionResult> Logout()
{
    var token = HttpContext.Request.Headers["Authorization"]
        .ToString().Replace("Bearer ", "");

    var jti = User.FindFirst(JwtRegisteredClaimNames.Jti)?.Value;
    await _cache.SetStringAsync($"blacklist:{jti}", "revoked",
        new DistributedCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(15)
        });

    return Ok("Çıkış yapıldı");
}

// Middleware ile blacklist kontrolü
public class TokenBlacklistMiddleware
{
    public async Task InvokeAsync(HttpContext context, IDistributedCache cache)
    {
        var jti = context.User.FindFirst(JwtRegisteredClaimNames.Jti)?.Value;
        if (jti != null && await cache.GetStringAsync($"blacklist:{jti}") != null)
        {
            context.Response.StatusCode = 401;
            return;
        }
        await _next(context);
    }
}