Tüm yazılar

ASP.NET Core Uygulamalarında Güvenlik: Kimlik Doğrulama, JWT ve OWASP Kontrol Listesi

ASP.NET Core uygulamalarını güvenli hale getirmek için kimlik doğrulama, JWT, yetkilendirme politikaları, gizli bilgilerin yönetimi, rate limiting ve OWASP Top 10 risklerine karşı alınacak önlemleri adım adım anlatıyoruz.

ASP.NET Core Uygulamalarında Güvenlik: Kimlik Doğrulama, JWT ve OWASP Kontrol Listesi

Bir uygulamanın kaç özelliği olduğu, tek bir güvenlik açığıyla kaybedilen güvenin yanında önemini yitirir. İyi haber şu ki ASP.NET Core, güvenli varsayılanlarla gelen ve yaygın saldırılara karşı hazır mekanizmalar sunan bir framework. Kötü haber: bu mekanizmaları yanlış yapılandırmak ya da bilmeden devre dışı bırakmak çok kolay. Bu yazıda kimlik doğrulamadan gizli bilgilerin yönetimine, OWASP Top 10 risklerinden production kontrol listesine kadar pratik bir güvenlik rehberi hazırladık.

Kimlik doğrulama ve yetkilendirme: iki ayrı soru

Güvenliğin temelinde birbirine sık karıştırılan iki kavram vardır:

  • Authentication (kimlik doğrulama): "Sen kimsin?" Kullanıcının iddia ettiği kişi olduğunu doğrulamak.
  • Authorization (yetkilendirme): "Bunu yapmaya iznin var mı?" Kimliği doğrulanmış kullanıcının bir kaynağa erişip erişemeyeceğine karar vermek.

ASP.NET Core'da bu iki adım ayrı middleware'lerle yapılır ve sıraları önemlidir:

app.UseRouting();
app.UseAuthentication(); // önce kim olduğunu bul
app.UseAuthorization();  // sonra iznini kontrol et
app.MapControllers();

Hangi yöntemi seçeceğiniz uygulamanın türüne bağlıdır:

Senaryo Önerilen yöntem Neden
Sunucu tarafında render edilen MVC/Razor uygulaması Cookie Tarayıcı otomatik yönetir, HttpOnly ile JavaScript'ten korunur
Aynı domaindeki SPA Cookie (BFF deseni) Token'ı tarayıcıda saklama riskini ortadan kaldırır
Mobil uygulama, servisler arası iletişim JWT Durumsuz, farklı istemcilerde kolay taşınır

JWT'yi "modern" olduğu için her yerde kullanmak yaygın bir hatadır. Tarayıcıdaki bir uygulama için, JWT'yi localStorage'da saklamak onu XSS saldırılarına açık hale getirir; HttpOnly cookie çoğu zaman daha güvenli bir seçenektir.

Dizüstü bilgisayar ekranını işaret eden eller
Doğru kimlik doğrulama yöntemi, uygulamanın kimler tarafından ve nasıl kullanıldığına göre seçilir.

JWT'yi doğru yapılandırmak

JWT kullanacaksanız token doğrulama parametrelerinin hiçbirini gevşek bırakmayın:

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(o =>
    {
        o.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidIssuer = "https://api.siteniz.com",
            ValidateAudience = true,
            ValidAudience = "siteniz-mobil",
            ValidateLifetime = true,
            ClockSkew = TimeSpan.FromSeconds(30),
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]!))
        };
    });

JWT ile ilgili temel kurallar:

  • Kısa ömürlü access token (ör. 10-15 dakika) ve daha uzun ömürlü, sunucuda iptal edilebilen bir refresh token kullanın.
  • Token içine hassas bilgi koymayın. JWT şifreli değildir, sadece imzalıdır; içeriğini herkes okuyabilir.
  • İmza anahtarı en az 256 bit olsun ve asla kaynak koduna yazılmasın.
  • Refresh token'ı her kullanımda yenileyin (rotation); çalınan eski bir token tekrar kullanılırsa tüm oturumu iptal edin.

Şifreleri asla kendiniz saklamayın

Kendi kullanıcı tablonuzu ve şifre hash'leme mantığınızı yazmak, güvenlikte en riskli "tekerleği yeniden icat etme" girişimlerinden biridir. ASP.NET Core Identity; güçlü şifre hash'leme, hesap kilitleme, e-posta doğrulama, iki faktörlü doğrulama ve güvenlik damgası (security stamp) gibi özellikleri hazır sunar. Kendi sisteminizi yazmak zorundaysanız en azından PasswordHasher<T> sınıfını kullanın; şifreleri MD5 veya SHA256 gibi hızlı hash'lerle saklamayın.

Politika tabanlı yetkilendirme

[Authorize(Roles = "Admin")] basit senaryolarda yeterlidir, ama kurallar karmaşıklaştıkça policy kullanmak daha temiz ve test edilebilir bir yapı sunar:

builder.Services.AddAuthorizationBuilder()
    .AddPolicy("CanManageOrders", p => p.RequireRole("Admin", "Operations"))
    .AddPolicy("VerifiedCustomer", p => p.RequireClaim("email_verified", "true"));

[Authorize(Policy = "CanManageOrders")]
public class AdminOrdersController : ControllerBase { }

IDOR'a dikkat

En sık gözden kaçan açıklardan biri Insecure Direct Object Reference'tır. Kullanıcı giriş yapmış ama GET /api/orders/1052 isteğinde 1052 numaralı siparişin gerçekten ona ait olup olmadığı kontrol edilmiyorsa, sadece URL'deki sayıyı değiştirerek başkalarının siparişleri görülebilir:

var order = await db.Orders.FirstOrDefaultAsync(o => o.Id == id && o.CustomerId == currentUserId);
if (order is null) return NotFound(); // 403 yerine 404: kaynağın varlığını da sızdırma

Her veri erişiminde "bu kayıt bu kullanıcıya mı ait?" sorusunu sorun.

Gizli bilgiler koda girmez

Bağlantı cümleleri, API anahtarları ve JWT imza anahtarları appsettings.json içinde Git'e girmemelidir.

  • Geliştirmede User Secrets kullanın: dotnet user-secrets set "Jwt:Key" "...".
  • Production'da ortam değişkenleri veya Azure Key Vault, AWS Secrets Manager gibi bir gizli bilgi kasası kullanın.
  • Yanlışlıkla commit edilen bir anahtarı silmek yetmez; Git geçmişinde kalır. Anahtarı hemen iptal edip yenileyin.

Karanlık ekranda terminal komut satırı
Bir sunucuya erişimi olan herkesin her şeye erişimi olmamalı: en az yetki prensibi.

OWASP Top 10 ve ASP.NET Core'daki karşılıkları

OWASP Top 10, web uygulamalarındaki en kritik güvenlik risklerinin listesidir. ASP.NET Core'da bunlara karşı neler yapabileceğinizi özetleyelim:

Kırık erişim kontrolü: Varsayılan olarak tüm endpoint'leri korumaya alın, herkese açık olanları açıkça işaretleyin:

builder.Services.AddAuthorizationBuilder()
    .SetFallbackPolicy(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build());
// Herkese açık endpoint'lerde: [AllowAnonymous]

Enjeksiyon (SQL Injection): EF Core LINQ sorguları parametreli çalışır. Ham SQL gerektiğinde FromSql($"... {id}") gibi interpolasyonlu metotları kullanın; asla string birleştirerek SQL oluşturmayın.

XSS (Cross-Site Scripting): Razor, @ ile yazdığınız her değeri otomatik olarak HTML-encode eder. Html.Raw() kullandığınız her yer potansiyel bir risktir; kullanıcıdan gelen Markdown veya HTML'i render ediyorsanız mutlaka sanitize edin. Ek koruma için Content-Security-Policy başlığı ekleyin.

CSRF: Cookie tabanlı kimlik doğrulamada form gönderimlerinde antiforgery token zorunlu olmalı. MVC'de AutoValidateAntiforgeryTokenAttribute'u global filtre olarak eklemek bunu tek satırda çözer. Cookie'lerde SameSite=Lax veya Strict kullanın.

Güvenlik yapılandırma hataları: Production'da Developer Exception Page'i kapatın, HTTPS ve HSTS'yi açın, gereksiz HTTP başlıklarını (Server, X-Powered-By) kaldırın.

Güncel olmayan bileşenler: dotnet list package --vulnerable komutu, bilinen güvenlik açığı bulunan NuGet paketlerini listeler. Bunu CI sürecinize ekleyin.

Rate limiting ile kaba kuvvete karşı

Giriş, şifre sıfırlama ve iletişim formu gibi endpoint'ler otomatik saldırıların ilk hedefidir. .NET 7 ile gelen yerleşik rate limiter bunu kolaylaştırır:

builder.Services.AddRateLimiter(o =>
{
    o.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
    o.AddFixedWindowLimiter("login", l =>
    {
        l.PermitLimit = 5;
        l.Window = TimeSpan.FromMinutes(1);
    });
});

app.UseRateLimiter();
app.MapPost("/api/auth/login", Login).RequireRateLimiting("login");

Buna ek olarak Identity'nin hesap kilitleme (lockout) özelliğini açmak, aynı hesaba yönelik deneme saldırılarını yavaşlatır.

Güvenlik başlıkları

Birkaç HTTP başlığı, tarayıcıyı sizin adınıza bir savunma katmanına dönüştürür:

app.Use(async (ctx, next) =>
{
    var h = ctx.Response.Headers;
    h["X-Content-Type-Options"] = "nosniff";
    h["Referrer-Policy"] = "strict-origin-when-cross-origin";
    h["X-Frame-Options"] = "DENY";
    h["Content-Security-Policy"] = "default-src 'self'; img-src 'self' https:; frame-ancestors 'none'";
    await next();
});

CSP'yi sıkı bir politikayla başlatıp tarayıcı konsolundaki ihlallere bakarak gerektiği kadar gevşetmek, en güvenli yaklaşımdır.

Loglama: neyi yazmalı, neyi asla yazmamalı?

Başarısız giriş denemeleri, yetki reddedilen istekler ve kritik işlemler (şifre değişikliği, rol atama) mutlaka loglanmalı. Ama şifreler, tam kart numaraları, token'lar ve kişisel veriler asla loga yazılmamalı. KVKK gibi düzenlemeler açısından da loglarınız bir veri sızıntısı kaynağı olmamalı.

Production öncesi kontrol listesi

  • HTTPS zorunlu, HSTS açık.
  • Tüm endpoint'ler varsayılan olarak korumalı, açık olanlar bilinçli olarak işaretli.
  • Her veri erişiminde sahiplik kontrolü yapılıyor (IDOR).
  • Gizli bilgiler kaynak kodunda değil, kasada veya ortam değişkenlerinde.
  • Form gönderimlerinde antiforgery, cookie'lerde HttpOnly + Secure + SameSite.
  • Giriş ve form endpoint'lerinde rate limiting.
  • Güvenlik başlıkları ve CSP tanımlı.
  • Bilinen açığı olan paket yok.
  • Hata sayfaları stack trace göstermiyor.

Sonuç

Güvenlik, projenin sonunda eklenen bir özellik değil, her satırda verilen küçük kararların toplamıdır. ASP.NET Core bu kararların çoğunu sizin için güvenli varsayılanlarla verir; sizin işiniz bu varsayılanları korumak, kimlik doğrulama yöntemini doğru seçmek ve her veri erişiminde "bu kişinin buna hakkı var mı?" sorusunu sormaktır. Yukarıdaki kontrol listesini her yayından önce gözden geçirmek, olası bir krizin büyük kısmını daha başlamadan önler.

Sık sorulan sorular

JWT'yi nerede saklamalıyım?
Mobil uygulamalarda platformun güvenli depolama alanında (Keychain/Keystore). Tarayıcıda ise mümkünse token'ı hiç JavaScript'e vermeyip HttpOnly cookie ile çalışmak daha güvenlidir.

Kullanıcı çıkış yaptığında JWT'yi nasıl geçersiz kılarım?
JWT durumsuz olduğu için süresi dolana kadar geçerlidir. Bu yüzden access token ömrünü kısa tutup, çıkışta sunucudaki refresh token'ı iptal etmek en pratik yöntemdir.

HTTPS yerel geliştirmede de gerekli mi?
Evet, production'a benzer bir ortamda geliştirmek sürprizleri azaltır. dotnet dev-certs https --trust komutu yerel geliştirme sertifikasını kurar.

↑↓ gezEnter seç