ASP.NET Core API Güvenliğine Giriş
Günümüzün modern uygulama mimarilerinde API'ler, farklı sistemler ve servisler arasında veri alışverişinin temelini oluşturmaktadır. Mobil uygulamalar, tek sayfa uygulamaları (SPA'lar), mikroservisler ve diğer arka uç servisleri, iş mantığını ve veriyi sunmak için API'lara güvenir. Bu kadar merkezi bir rol oynayan API'ların güvenliği, uygulamanın genel güvenliği için kritik öneme sahiptir. Bir API'deki güvenlik açığı, hassas verilerin ifşa olmasına, yetkisiz erişime, hizmet kesintilerine ve hatta sistemin tamamen ele geçirilmesine yol açabilir. Bu nedenle, ASP.NET Core ile geliştirilen API'ları güvenli hale getirmek, geliştirme sürecinin ayrılmaz bir parçası olmalıdır.
Bu kapsamlı rehberde, ASP.NET Core API güvenliğinin temel prensiplerini, kimlik doğrulama (authentication) ve yetkilendirme (authorization) mekanizmalarını, JWT (JSON Web Tokens) kullanımını, OAuth 2.0 entegrasyonlarını, ek güvenlik önlemlerini ve en iyi uygulamaları derinlemesine inceleyeceğiz. Amacımız, API'larınızı potansiyel tehditlere karşı korumanıza yardımcı olacak pratik bilgiler ve örnekler sunmaktır. Güvenli bir API altyapısı kurmak, sadece yasal uyumluluk için değil, aynı zamanda kullanıcı güvenini sağlamak ve marka itibarınızı korumak için de zorunludur.
Temel Güvenlik Kavramları
API güvenliğini anlamak için iki temel kavramı iyi ayırt etmek gerekir: Kimlik Doğrulama (Authentication) ve Yetkilendirme (Authorization).
* Kimlik Doğrulama (Authentication): "Sen kimsin?" sorusuna cevap verir. Bir kullanıcının veya servisin iddia ettiği kimliği doğrulama sürecidir. Genellikle kullanıcı adı/şifre, tokenlar, API anahtarları veya sertifikalar gibi kimlik bilgilerinin sunulması ve sistem tarafından doğrulanması ile gerçekleşir. Kimlik doğrulamanın başarılı olması durumunda, sistem o varlığın kimliğini tanır ve bir kimlik (identity) oluşturur. Bu kimlik, sonraki yetkilendirme adımlarında kullanılır.
* Yetkilendirme (Authorization): "Ne yapmana izin var?" sorusuna cevap verir. Kimliği doğrulanmış bir kullanıcının veya servisin belirli bir kaynağa veya işleme erişim yetkisinin olup olmadığını belirleme sürecidir. Kullanıcının rolleri, politikaları veya özel talepleri (claims) temel alınarak verilir. Örneğin, bir "Admin" rolüne sahip kullanıcının tüm API uç noktalarına erişimi olabilirken, "Guest" kullanıcının sadece belirli okuma işlemlerine izin verilebilir. Bu, en az yetki prensibinin (Principle of Least Privilege) temelini oluşturur.
Bu iki kavram, birlikte çalışarak API'larınızı güvenli tutar. Bir API isteği geldiğinde, önce kimlik doğrulanır (kimlik kanıtlanır), sonra bu kimliğin (veya ilgili taleplerinin) istenen işlemi yapma yetkisinin olup olmadığı yetkilendirme ile kontrol edilir.
Yaygın API Güvenlik Tehditleri (OWASP API Security Top 10'dan İlhamla)
API'lar, kendine özgü bir dizi güvenlik riski taşır. OWASP API Security Top 10 gibi kaynaklar bu tehditleri sınıflandırmıştır:
Bu tehditleri göz önünde bulundurarak ASP.NET Core API'larımızı tasarlamalı ve geliştirmeliyiz.
ASP.NET Core'da Kimlik Doğrulama Mekanizmaları
ASP.NET Core, farklı kimlik doğrulama şemalarını destekleyen esnek bir mimariye sahiptir. En yaygın kullanılanlardan bazıları şunlardır:
JSON Web Tokens (JWT) ile Kimlik Doğrulama
JWT'ler, ASP.NET Core API'larında kimlik doğrulaması için en popüler ve etkili yöntemlerden biridir. JWT'ler, taraflar arasında güvenli bir şekilde bilgi alışverişi yapmak için kullanılan kompakt, URL güvenli bir yoldur.
JWT Nasıl Çalışır?
Bir JWT üç bölümden oluşur, bunlar noktalarla ayrılır: Header (Başlık), Payload (Yük) ve Signature (İmza). Bu yapısı sayesinde self-contained (kendi içinde yeterli) bir yapı sunar.
1. Header (Başlık): Tokenın tipi (JWT) ve kullanılan imzalama algoritması (örn. HS256 veya RS256) gibi meta verileri içerir. Base64URL ile kodlanır.
2. Payload (Yük): "Claim" adı verilen ifadeleri içerir. Bir claim, bir varlık (genellikle kullanıcı) hakkında bir ifade veya ek veri parçacığıdır. Kayıtlı (registered), herkese açık (public) ve özel (private) claimler olabilir. Örneğin, kullanıcı ID'si (`sub`), e-posta (`email`), roller (`roles`) gibi bilgiler burada bulunur. Tokenın geçerlilik süresi (`exp`) gibi bilgiler de payload'da yer alır. Payload da Base64URL ile kodlanır.
3. Signature (İmza): Header ve Payload'ın base64URL kodlu halleri ile sunucu tarafından bilinen bir sır (secret) veya özel anahtar kullanılarak oluşturulur. Bu imza, tokenın istemci tarafından değiştirilmediğini ve gerçekten belirtilen gönderici tarafından oluşturulduğunu doğrulamak için kullanılır. İmza sayesinde tokenın bütünlüğü ve orijinalliği garanti altına alınır. İmza kısmı olmadan JWT'nin güvenliği kalmaz.
ASP.NET Core'da JWT Kimlik Doğrulaması
ASP.NET Core'da JWT kimlik doğrulamasını yapılandırmak için `Microsoft.AspNetCore.Authentication.JwtBearer` NuGet paketini kullanırız.
Öncelikle `Program.cs` (veya eski versiyonlarda `Startup.cs`) dosyasında gerekli servisleri eklememiz gerekir:
Ve `appsettings.json` dosyasında JWT ayarları:
Önemli Not: `Secret` anahtarı çok gizli tutulmalı ve asla kaynak kodda doğrudan yayınlanmamalıdır. Üretim ortamlarında Azure Key Vault veya çevre değişkenleri gibi güvenli yöntemlerle yönetilmelidir. Anahtarınızın karmaşıklığı ve uzunluğu, bruteforce saldırılarına karşı direncini artırır.
JWT Oluşturma
Kullanıcı başarılı bir şekilde kimlik doğrulandıktan sonra (örneğin kullanıcı adı/şifre ile), sunucu bir JWT oluşturur ve istemciye gönderir. İstemci bu tokenı sonraki isteklerinde `Authorization` başlığında `Bearer <token>` formatında gönderir.
Refresh Token Mekanizması
JWT'ler genellikle kısa ömürlü olmalıdır (örn. 15-30 dakika). Bunun nedeni, bir tokenın ele geçirilmesi durumunda saldırganın erişim süresini kısıtlamaktır. Ancak, her kısa sürede kullanıcıdan yeniden kimlik doğrulaması istenmesi kullanıcı deneyimini bozar. Bu sorunu çözmek için Refresh Token mekanizması kullanılır.
Refresh tokenlar, access tokenlardan daha uzun ömürlüdür ve istemcinin yeni bir access token almak için kullanabileceği özel bir tokendır. Refresh tokenlar veritabanında saklanır ve kullanıldığında doğrulanır, tek kullanımlık hale getirilebilir veya belirli bir süre sonra geçersiz kılınabilir. Bu, API güvenliğini artırırken kullanıcı deneyimini korur. İstemci, access token süresi dolduğunda refresh token ile kimlik sunucusuna başvurur ve yeni bir access token (ve muhtemelen yeni bir refresh token) alır.
OAuth 2.0 ve OpenID Connect
OAuth 2.0 bir yetkilendirme çerçevesidir, kimlik doğrulama protokolü değildir. Kullanıcının kendi kimlik bilgilerini üçüncü taraf uygulamalarla paylaşmak zorunda kalmadan, bir uygulamanın başka bir servis üzerindeki korunan kaynaklara erişmesine izin veren bir yöntem sunar. Örneğin, bir web sitesinin Google hesabınızdaki takviminize erişim izni istemesi gibi. OAuth 2.0, yetki verme (delegated authorization) prensibi üzerine kuruludur ve birçok farklı akışı (Authorization Code Flow, Client Credentials Flow vb.) destekler.
OpenID Connect (OIDC) ise OAuth 2.0 üzerine inşa edilmiş bir kimlik doğrulama katmanıdır. OAuth 2.0'ın yetkilendirme yeteneklerini kullanarak, kullanıcıların kimliğini doğrulamak ve temel profil bilgilerini güvenli ve birlikte çalışabilir bir şekilde almak için standart bir yol sağlar. Özellikle tek oturum açma (SSO) senaryolarında yaygın olarak kullanılır. OIDC, kimlik sağlayıcılarından ID Token adı verilen JWT'ler döndürür, bu tokenlar kullanıcı hakkında doğrulanmış bilgileri içerir.
ASP.NET Core, IdentityServer gibi ürünlerle veya Azure AD, Auth0 gibi bulut tabanlı kimlik sağlayıcılarla entegrasyon için kolaylıklar sunar. Bu entegrasyonlar genellikle `AddOpenIdConnect` veya `AddMicrosoftIdentityWebApp` gibi yöntemlerle yapılandırılır ve genellikle harici bir kimlik sağlayıcıya yönlendirme içerir.
API Anahtarı Kimlik Doğrulaması
Daha basit API'ler veya servisten servise güvenli iletişim için API anahtarları kullanılabilir. Bu yöntemde, istemci genellikle `X-API-KEY` gibi özel bir HTTP başlığında veya sorgu parametresi olarak bir anahtar gönderir. Sunucu, bu anahtarı doğrulayarak isteği kabul eder veya reddeder. Anahtarların genellikle veritabanında saklanması ve gelen istekle eşleştirilmesi gerekir.
Dezavantajları:
Genellikle daha kapsamlı bir kimlik doğrulama çözümü gerektirmeyen, dahili servisler arası iletişim veya düşük güvenlik gerektiren halka açık, okuma amaçlı API'lar için tercih edilebilir.
ASP.NET Core'da Yetkilendirme
Kimlik doğrulama sonrası, bir kullanıcının belirli bir kaynağa erişim yetkisinin olup olmadığını belirlemek için yetkilendirme kullanılır. ASP.NET Core iki ana yetkilendirme türü sunar:
1. Rol Tabanlı Yetkilendirme (Role-Based Authorization): Kullanıcılara roller atanır (örn. Admin, Editor, User). Bu roller daha sonra kontrolcülerde veya eylem yöntemlerinde erişimi kısıtlamak için `[Authorize(Roles = "RolAdi")]` niteliği kullanılarak uygulanır.
Bu yöntem, basit senaryolar için yeterli olsa da, karmaşık yetkilendirme kuralları için yetersiz kalabilir.
2. Politika Tabanlı Yetkilendirme (Policy-Based Authorization): Daha esnek ve güçlü bir yaklaşımdır. Kimlik doğrulanmış bir kullanıcının bir kaynağa erişmek için karşılaması gereken bir veya daha fazla gereksinimi (requirement) tanımlayan bir politika oluşturulur. Bu, yetkilendirme mantığını uygulamanın geri kalanından ayırarak daha sürdürülebilir bir yapı sunar.
Politika tabanlı yetkilendirme için `Program.cs`'de politikaları tanımlarız:
Ve kontrolcüde bu politikayı `[Authorize(Policy = "PolitikaAdi")]` kullanarak uygularız:
Özel Politika Gereksinimleri ve İşleyicileri
Politika tabanlı yetkilendirme, özel gereksinimler (requirements) ve bu gereksinimleri işleyen işleyiciler (handlers) tanımlamanıza olanak tanır. Bu, çok karmaşık yetkilendirme mantıklarını dahi API katmanından bağımsız olarak yönetmenizi sağlar.
Örnek bir özel gereksinim:
Ve bu gereksinimi işleyen işleyici:
Bu işleyiciyi `Program.cs`'de DI konteynerine kaydetmeyi unutmayın:
Bu yapı, yetkilendirme kurallarınızı temiz, test edilebilir ve yeniden kullanılabilir bir şekilde uygulamanıza olanak tanır.
Ek Güvenlik Önlemleri ve En İyi Uygulamalar
API güvenliği sadece kimlik doğrulama ve yetkilendirme ile sınırlı değildir. Kapsamlı bir güvenlik duruşu için birçok başka önlemin alınması gerekir.
Güvenli API Tasarım Prensipleri
API'larınızı tasarlarken de güvenliği göz önünde bulundurmak kritik önem taşır:
Sonuç
ASP.NET Core ile güçlü ve güvenli API'lar geliştirmek mümkündür, ancak bu sürekli dikkat ve en iyi uygulamaların titizlikle uygulanmasını gerektirir. Kimlik doğrulama ve yetkilendirme mekanizmalarını doğru bir şekilde yapılandırmak, JWT'lerin güvenli kullanımını anlamak ve ek güvenlik önlemlerini (HTTPS, CORS, girdi doğrulama, oran sınırlama, sır yönetimi, güvenlik başlıkları) almak, API'larınızın potansiyel saldırılara karşı direncini önemli ölçüde artıracaktır.
Güvenlik, geliştirme sürecinin sonuna bırakılacak bir düşünce değil, tasarım aşamasından itibaren entegre edilmesi gereken bir temel bileşendir. Unutmayın, API'larınız ne kadar güvenli olursa, uygulamanız ve kullanıcılarınızın verileri de o kadar güvende olur. Bu rehberde paylaşılan bilgiler ve stratejiler, ASP.NET Core API'larınız için sağlam bir güvenlik temeli oluşturmanıza yardımcı olacaktır. Güvenli kodlama alışkanlıkları ve sürekli öğrenme ile API'larınızın gelecekteki tehditlere karşı da hazırlıklı olmasını sağlayabilirsiniz. Güvenlik standartlarını takip etmek (örn. OWASP Top 10), düzenli güvenlik denetimleri yapmak ve ekibinizi güvenlik konusunda eğitmek, uzun vadede API'larınızın güvenliğini sağlamanın anahtarıdır.
Günümüzün modern uygulama mimarilerinde API'ler, farklı sistemler ve servisler arasında veri alışverişinin temelini oluşturmaktadır. Mobil uygulamalar, tek sayfa uygulamaları (SPA'lar), mikroservisler ve diğer arka uç servisleri, iş mantığını ve veriyi sunmak için API'lara güvenir. Bu kadar merkezi bir rol oynayan API'ların güvenliği, uygulamanın genel güvenliği için kritik öneme sahiptir. Bir API'deki güvenlik açığı, hassas verilerin ifşa olmasına, yetkisiz erişime, hizmet kesintilerine ve hatta sistemin tamamen ele geçirilmesine yol açabilir. Bu nedenle, ASP.NET Core ile geliştirilen API'ları güvenli hale getirmek, geliştirme sürecinin ayrılmaz bir parçası olmalıdır.
Bu kapsamlı rehberde, ASP.NET Core API güvenliğinin temel prensiplerini, kimlik doğrulama (authentication) ve yetkilendirme (authorization) mekanizmalarını, JWT (JSON Web Tokens) kullanımını, OAuth 2.0 entegrasyonlarını, ek güvenlik önlemlerini ve en iyi uygulamaları derinlemesine inceleyeceğiz. Amacımız, API'larınızı potansiyel tehditlere karşı korumanıza yardımcı olacak pratik bilgiler ve örnekler sunmaktır. Güvenli bir API altyapısı kurmak, sadece yasal uyumluluk için değil, aynı zamanda kullanıcı güvenini sağlamak ve marka itibarınızı korumak için de zorunludur.
Temel Güvenlik Kavramları
API güvenliğini anlamak için iki temel kavramı iyi ayırt etmek gerekir: Kimlik Doğrulama (Authentication) ve Yetkilendirme (Authorization).
* Kimlik Doğrulama (Authentication): "Sen kimsin?" sorusuna cevap verir. Bir kullanıcının veya servisin iddia ettiği kimliği doğrulama sürecidir. Genellikle kullanıcı adı/şifre, tokenlar, API anahtarları veya sertifikalar gibi kimlik bilgilerinin sunulması ve sistem tarafından doğrulanması ile gerçekleşir. Kimlik doğrulamanın başarılı olması durumunda, sistem o varlığın kimliğini tanır ve bir kimlik (identity) oluşturur. Bu kimlik, sonraki yetkilendirme adımlarında kullanılır.
Kimlik doğrulama, bir varlığın (kullanıcı veya servis) gerçekten iddia ettiği kişi veya şey olduğunu kanıtlama sürecidir.
* Yetkilendirme (Authorization): "Ne yapmana izin var?" sorusuna cevap verir. Kimliği doğrulanmış bir kullanıcının veya servisin belirli bir kaynağa veya işleme erişim yetkisinin olup olmadığını belirleme sürecidir. Kullanıcının rolleri, politikaları veya özel talepleri (claims) temel alınarak verilir. Örneğin, bir "Admin" rolüne sahip kullanıcının tüm API uç noktalarına erişimi olabilirken, "Guest" kullanıcının sadece belirli okuma işlemlerine izin verilebilir. Bu, en az yetki prensibinin (Principle of Least Privilege) temelini oluşturur.
Bu iki kavram, birlikte çalışarak API'larınızı güvenli tutar. Bir API isteği geldiğinde, önce kimlik doğrulanır (kimlik kanıtlanır), sonra bu kimliğin (veya ilgili taleplerinin) istenen işlemi yapma yetkisinin olup olmadığı yetkilendirme ile kontrol edilir.
Yaygın API Güvenlik Tehditleri (OWASP API Security Top 10'dan İlhamla)
API'lar, kendine özgü bir dizi güvenlik riski taşır. OWASP API Security Top 10 gibi kaynaklar bu tehditleri sınıflandırmıştır:
- Broken Object Level Authorization (BOLA / API1:2023): En yaygın ve kritik zafiyetlerden biridir. Bir kullanıcının, doğrudan nesne referansları üzerinden (örn. URL'deki ID) yetkili olmadığı verilere erişmesidir. Örneğin, bir kullanıcının başkasının sipariş detaylarını görmesi.
- Broken Authentication (API2:2023): Kimlik doğrulama mekanizmalarındaki zafiyetler (zayıf şifreler, eksik MFA, JWT imza hataları) kimlik bilgilerinin ele geçirilmesine yol açar.
- Excessive Data Exposure (API3:2023): Geliştiricilerin istemcilere gereğinden fazla veri göndermesi. İstemci tarafında filtrelenmesi beklenen hassas verilerin açığa çıkması.
- Unrestricted Resource Consumption (API4:2023): API'ların kaynak kullanımının (CPU, bellek, disk, bant genişliği) sınırlanmaması, hizmet reddi (DoS) saldırılarına yol açabilir.
- Broken Function Level Authorization (BFLA / API5:2023): Yetkisiz kullanıcıların, yetkili oldukları fonksiyonlara erişmeleri gereken kısıtlamaları aşarak kritik fonksiyonlara (örn. yönetici paneli işlevleri) erişmesidir.
- Server Side Request Forgery (SSRF / API6:2023): API'nın dışarıdan sağlanan URL'leri işlediği durumlarda, saldırganın API'yi dahili ağdaki diğer kaynaklara istek yapmaya zorlaması.
- Security Misconfiguration (API7:2023): Güvenlik ayarlarının yanlış yapılması (örn. varsayılan şifreler, açık hata mesajları, gereksiz portların açık olması).
- Code Injection (API8:2023): Veritabanı sorguları (SQL Injection), komut yorumlayıcıları veya diğer API'lar aracılığıyla kod enjekte etme.
- Improper Inventory Management (API9:2023): API versiyonlarının güncel olmaması, eski/kullanılmayan API uç noktalarının silinmemesi, güvenlik açıklarının fark edilmemesine yol açar.
- Unsafe Consumption of APIs (API10:2023): Bir API'nın başka API'ları güvenli olmayan bir şekilde kullanması.
Bu tehditleri göz önünde bulundurarak ASP.NET Core API'larımızı tasarlamalı ve geliştirmeliyiz.
ASP.NET Core'da Kimlik Doğrulama Mekanizmaları
ASP.NET Core, farklı kimlik doğrulama şemalarını destekleyen esnek bir mimariye sahiptir. En yaygın kullanılanlardan bazıları şunlardır:
- Taşıyıcı Token Kimlik Doğrulaması (Bearer Token Authentication): Genellikle JWT (JSON Web Tokens) ile birlikte kullanılır. Stateless (durumsuz) API'lar için idealdir.
- API Anahtarı Kimlik Doğrulaması (API Key Authentication): Daha basit senaryolar veya servisten servise iletişim için kullanılabilir.
- OAuth 2.0 ve OpenID Connect: Daha karmaşık senaryolar, üçüncü taraf entegrasyonları ve merkezi kimlik yönetimi için.
JSON Web Tokens (JWT) ile Kimlik Doğrulama
JWT'ler, ASP.NET Core API'larında kimlik doğrulaması için en popüler ve etkili yöntemlerden biridir. JWT'ler, taraflar arasında güvenli bir şekilde bilgi alışverişi yapmak için kullanılan kompakt, URL güvenli bir yoldur.
JWT Nasıl Çalışır?
Bir JWT üç bölümden oluşur, bunlar noktalarla ayrılır: Header (Başlık), Payload (Yük) ve Signature (İmza). Bu yapısı sayesinde self-contained (kendi içinde yeterli) bir yapı sunar.
1. Header (Başlık): Tokenın tipi (JWT) ve kullanılan imzalama algoritması (örn. HS256 veya RS256) gibi meta verileri içerir. Base64URL ile kodlanır.
Kod:
{
"alg": "HS256",
"typ": "JWT"
}
Kod:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"email": "john.doe@example.com",
"exp": 1678886400, // Tokenın bitiş zamanı (Unix timestamp)
"iat": 1678800000 // Tokenın yayınlanma zamanı
}
ASP.NET Core'da JWT Kimlik Doğrulaması
ASP.NET Core'da JWT kimlik doğrulamasını yapılandırmak için `Microsoft.AspNetCore.Authentication.JwtBearer` NuGet paketini kullanırız.
Öncelikle `Program.cs` (veya eski versiyonlarda `Startup.cs`) dosyasında gerekli servisleri eklememiz gerekir:
Kod:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using System.Security.Claims; // ClaimTypes için
var builder = WebApplication.CreateBuilder(args);
// JWT Ayarlarını appsettings.json'dan alabiliriz
var jwtSettings = builder.Configuration.GetSection("JwtSettings");
var secretKey = jwtSettings["Secret"] ?? throw new ArgumentNullException("JWT Secret key not found.");
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true, // Issuer'ı doğrula
ValidateAudience = true, // Audience'ı doğrula
ValidateLifetime = true, // Token ömrünü doğrula (exp claim)
ValidateIssuerSigningKey = true, // İmzalama anahtarını doğrula
ValidIssuer = jwtSettings["Issuer"], // Tokenı yayınlayan sunucu (örn. "myapiauthserver.com")
ValidAudience = jwtSettings["Audience"], // Tokenın geçerli olduğu alıcılar (örn. "mywebclient.com")
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey)),
ClockSkew = TimeSpan.Zero // Varsayılan 5 dakikalık toleransı sıfırla
};
options.Events = new JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
// Kimlik doğrulama hatası durumunda loglama veya özel yanıt
Console.WriteLine("Authentication failed: " + context.Exception.Message);
return Task.CompletedTask;
},
OnTokenValidated = context =>
{
// Token doğrulandığında ek işlemler yapabiliriz
var claimsIdentity = (ClaimsIdentity)context.Principal?.Identity;
if (claimsIdentity != null && claimsIdentity.HasClaim(c => c.Type == "admin_access" && c.Value == "true"))
{
// Özel bir claim kontrolü
}
return Task.CompletedTask;
}
};
});
builder.Services.AddAuthorization(); // Yetkilendirme servisini ekle
// Diğer servisler...
builder.Services.AddControllers();
var app = builder.Build();
// Middleware sırası önemlidir: Authentication, Authorization'dan önce gelmeli
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
Ve `appsettings.json` dosyasında JWT ayarları:
Kod:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"JwtSettings": {
"Secret": "bu_bir_cok_guclu_ve_minimum_32_karakter_uzunlugunda_gizli_anahtar_olmalidir",
"Issuer": "https://api.myapp.com",
"Audience": "https://client.myapp.com"
}
}
Önemli Not: `Secret` anahtarı çok gizli tutulmalı ve asla kaynak kodda doğrudan yayınlanmamalıdır. Üretim ortamlarında Azure Key Vault veya çevre değişkenleri gibi güvenli yöntemlerle yönetilmelidir. Anahtarınızın karmaşıklığı ve uzunluğu, bruteforce saldırılarına karşı direncini artırır.
JWT Oluşturma
Kullanıcı başarılı bir şekilde kimlik doğrulandıktan sonra (örneğin kullanıcı adı/şifre ile), sunucu bir JWT oluşturur ve istemciye gönderir. İstemci bu tokenı sonraki isteklerinde `Authorization` başlığında `Bearer <token>` formatında gönderir.
Kod:
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Microsoft.IdentityModel.Tokens;
using System.Text;
public class JwtService
{
private readonly IConfiguration _configuration;
public JwtService(IConfiguration configuration)
{
_configuration = configuration;
}
public string GenerateToken(string userId, string userName, IList<string> roles)
{
var jwtSettings = _configuration.GetSection("JwtSettings");
var secretKey = jwtSettings["Secret"] ?? throw new ArgumentNullException("JWT Secret key not found.");
var issuer = jwtSettings["Issuer"];
var audience = jwtSettings["Audience"];
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, userId),
new Claim(ClaimTypes.Name, userName)
};
foreach (var role in roles)
{
claims.Add(new Claim(ClaimTypes.Role, role));
}
claims.Add(new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())); // JWT ID
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: issuer,
audience: audience,
claims: claims,
expires: DateTime.UtcNow.AddMinutes(30), // Token ömrü (kısa tutulmalı)
signingCredentials: creds
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
Refresh Token Mekanizması
JWT'ler genellikle kısa ömürlü olmalıdır (örn. 15-30 dakika). Bunun nedeni, bir tokenın ele geçirilmesi durumunda saldırganın erişim süresini kısıtlamaktır. Ancak, her kısa sürede kullanıcıdan yeniden kimlik doğrulaması istenmesi kullanıcı deneyimini bozar. Bu sorunu çözmek için Refresh Token mekanizması kullanılır.
Refresh tokenlar, access tokenlardan daha uzun ömürlüdür ve istemcinin yeni bir access token almak için kullanabileceği özel bir tokendır. Refresh tokenlar veritabanında saklanır ve kullanıldığında doğrulanır, tek kullanımlık hale getirilebilir veya belirli bir süre sonra geçersiz kılınabilir. Bu, API güvenliğini artırırken kullanıcı deneyimini korur. İstemci, access token süresi dolduğunda refresh token ile kimlik sunucusuna başvurur ve yeni bir access token (ve muhtemelen yeni bir refresh token) alır.
OAuth 2.0 ve OpenID Connect
OAuth 2.0 bir yetkilendirme çerçevesidir, kimlik doğrulama protokolü değildir. Kullanıcının kendi kimlik bilgilerini üçüncü taraf uygulamalarla paylaşmak zorunda kalmadan, bir uygulamanın başka bir servis üzerindeki korunan kaynaklara erişmesine izin veren bir yöntem sunar. Örneğin, bir web sitesinin Google hesabınızdaki takviminize erişim izni istemesi gibi. OAuth 2.0, yetki verme (delegated authorization) prensibi üzerine kuruludur ve birçok farklı akışı (Authorization Code Flow, Client Credentials Flow vb.) destekler.
OpenID Connect (OIDC) ise OAuth 2.0 üzerine inşa edilmiş bir kimlik doğrulama katmanıdır. OAuth 2.0'ın yetkilendirme yeteneklerini kullanarak, kullanıcıların kimliğini doğrulamak ve temel profil bilgilerini güvenli ve birlikte çalışabilir bir şekilde almak için standart bir yol sağlar. Özellikle tek oturum açma (SSO) senaryolarında yaygın olarak kullanılır. OIDC, kimlik sağlayıcılarından ID Token adı verilen JWT'ler döndürür, bu tokenlar kullanıcı hakkında doğrulanmış bilgileri içerir.
ASP.NET Core, IdentityServer gibi ürünlerle veya Azure AD, Auth0 gibi bulut tabanlı kimlik sağlayıcılarla entegrasyon için kolaylıklar sunar. Bu entegrasyonlar genellikle `AddOpenIdConnect` veya `AddMicrosoftIdentityWebApp` gibi yöntemlerle yapılandırılır ve genellikle harici bir kimlik sağlayıcıya yönlendirme içerir.
API Anahtarı Kimlik Doğrulaması
Daha basit API'ler veya servisten servise güvenli iletişim için API anahtarları kullanılabilir. Bu yöntemde, istemci genellikle `X-API-KEY` gibi özel bir HTTP başlığında veya sorgu parametresi olarak bir anahtar gönderir. Sunucu, bu anahtarı doğrulayarak isteği kabul eder veya reddeder. Anahtarların genellikle veritabanında saklanması ve gelen istekle eşleştirilmesi gerekir.
Dezavantajları:
- Anahtarların güvenli bir şekilde saklanması ve iletilmesi gerekir. HTTPS zorunludur.
- Token gibi bütünlük garantisi sunmaz; anahtar bir kere ele geçirilirse, değiştirildiği anlaşılamaz.
- Ölçeklenebilirlik, anahtar yönetimi (oluşturma, iptal etme, dönüştürme) ve yetkilendirme mantığı daha karmaşık hale gelebilir.
- Her istemci için ayrı anahtarların oluşturulması ve izlenmesi gerekir.
Genellikle daha kapsamlı bir kimlik doğrulama çözümü gerektirmeyen, dahili servisler arası iletişim veya düşük güvenlik gerektiren halka açık, okuma amaçlı API'lar için tercih edilebilir.
ASP.NET Core'da Yetkilendirme
Kimlik doğrulama sonrası, bir kullanıcının belirli bir kaynağa erişim yetkisinin olup olmadığını belirlemek için yetkilendirme kullanılır. ASP.NET Core iki ana yetkilendirme türü sunar:
1. Rol Tabanlı Yetkilendirme (Role-Based Authorization): Kullanıcılara roller atanır (örn. Admin, Editor, User). Bu roller daha sonra kontrolcülerde veya eylem yöntemlerinde erişimi kısıtlamak için `[Authorize(Roles = "RolAdi")]` niteliği kullanılarak uygulanır.
Kod:
[Authorize(Roles = "Admin, Editor")] // Birden fazla rol virgülle ayrılabilir
public class AdminPanelController : ControllerBase
{
// Sadece Admin veya Editor rolleri olanlar erişebilir
[HttpGet("secret-data")]
public IActionResult GetSecretData()
{
return Ok("Gizli Yönetici Verisi");
}
}
[Authorize(Roles = "User")]
public class UserProfileController : ControllerBase
{
// Sadece User rolü olanlar erişebilir
[HttpGet("my-profile")]
public IActionResult GetMyProfile()
{
// Kullanıcının kendi profilini görmesine izin ver
return Ok($"Kullanıcı Profili için ID: {User.FindFirst(ClaimTypes.NameIdentifier)?.Value}");
}
}
2. Politika Tabanlı Yetkilendirme (Policy-Based Authorization): Daha esnek ve güçlü bir yaklaşımdır. Kimlik doğrulanmış bir kullanıcının bir kaynağa erişmek için karşılaması gereken bir veya daha fazla gereksinimi (requirement) tanımlayan bir politika oluşturulur. Bu, yetkilendirme mantığını uygulamanın geri kalanından ayırarak daha sürdürülebilir bir yapı sunar.
Politika tabanlı yetkilendirme için `Program.cs`'de politikaları tanımlarız:
Kod:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("RequireAdministratorRole",
policy => policy.RequireRole("Admin")); // Admin rolü gerektiren basit bir politika
options.AddPolicy("MustBeOver18",
policy => policy.RequireClaim("DateOfBirth", claim =>
{
// Doğum tarihi claim'i var mı ve kişi 18 yaşından büyük mü?
if (DateTime.TryParse(claim.Value, out DateTime dob))
{
return dob.AddYears(18) <= DateTime.Today;
}
return false;
}));
options.AddPolicy("CanEditProducts", policy =>
{
policy.Requirements.Add(new MinimumProductEditLevelRequirement(5));
});
});
Ve kontrolcüde bu politikayı `[Authorize(Policy = "PolitikaAdi")]` kullanarak uygularız:
Kod:
[Authorize(Policy = "RequireAdministratorRole")]
public class RestrictedController : ControllerBase
{
[HttpPost("admin-action")]
public IActionResult AdminAction()
{
return Ok("Bu eylemi sadece yöneticiler gerçekleştirebilir.");
}
}
[Authorize(Policy = "MustBeOver18")]
public class AgeRestrictedController : ControllerBase
{
[HttpGet("adult-content")]
public IActionResult GetAdultContent()
{
return Ok("Yetişkinlere özel içerik.");
}
}
Özel Politika Gereksinimleri ve İşleyicileri
Politika tabanlı yetkilendirme, özel gereksinimler (requirements) ve bu gereksinimleri işleyen işleyiciler (handlers) tanımlamanıza olanak tanır. Bu, çok karmaşık yetkilendirme mantıklarını dahi API katmanından bağımsız olarak yönetmenizi sağlar.
Örnek bir özel gereksinim:
Kod:
public class MinimumProductEditLevelRequirement : IAuthorizationRequirement
{
public int MinimumLevel { get; }
public MinimumProductEditLevelRequirement(int minimumLevel) => MinimumLevel = minimumLevel;
}
Ve bu gereksinimi işleyen işleyici:
Kod:
public class MinimumProductEditLevelHandler : AuthorizationHandler<MinimumProductEditLevelRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MinimumProductEditLevelRequirement requirement)
{
if (!context.User.HasClaim(c => c.Type == "ProductEditLevel"))
{
return Task.CompletedTask;
}
var productEditLevel = Convert.ToInt32(context.User.FindFirst(c => c.Type == "ProductEditLevel").Value);
if (productEditLevel >= requirement.MinimumLevel)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
Bu işleyiciyi `Program.cs`'de DI konteynerine kaydetmeyi unutmayın:
Kod:
builder.Services.AddSingleton<IAuthorizationHandler, MinimumProductEditLevelHandler>();
Ek Güvenlik Önlemleri ve En İyi Uygulamalar
API güvenliği sadece kimlik doğrulama ve yetkilendirme ile sınırlı değildir. Kapsamlı bir güvenlik duruşu için birçok başka önlemin alınması gerekir.
- HTTPS Kullanımı: Tüm API trafiği kesinlikle HTTPS üzerinden yapılmalıdır. Bu, verilerin ağ üzerinde şifreli olarak iletilmesini sağlar ve Man-in-the-Middle (MITM) saldırılarını önler. ASP.NET Core varsayılan olarak HTTPS'i destekler ve `app.UseHttpsRedirection()` ile tüm HTTP isteklerini HTTPS'e yönlendirir. Sertifika yönetimi (Let's Encrypt, Azure Key Vault sertifikaları) doğru yapılmalıdır.
Daha fazla bilgi için Microsoft Docs'u inceleyebilirsiniz.
- CORS (Cross-Origin Resource Sharing) Ayarları: API'nıza sadece belirli web sitelerinden (origin'lerden) gelen isteklerin erişmesine izin vermek için CORS'u doğru yapılandırın. `AllowAnyOrigin()` kullanmaktan şiddetle kaçının veya sadece güvenli ve kontrollü, halka açık API'lar için kullanın. Hangi HTTP metotlarına ve başlıklarına izin verildiğini de dikkatlice yapılandırın.
Kod:// Program.cs builder.Services.AddCors(options => { options.AddDefaultPolicy( policy => { // Sadece belirli güvenilir originlere izin verin policy.WithOrigins("https://www.example.com", "https://app.client.com") .AllowAnyHeader() // Hangi HTTP başlıklarına izin verileceğini belirtin .AllowAnyMethod() // Hangi HTTP metotlarına (GET, POST, PUT, DELETE) izin verileceğini belirtin .AllowCredentials(); // Eğer kimlik bilgileri (cookies, HTTP kimlik doğrulaması) ile istek yapılıyorsa }); }); // ... app.UseCors(); // Middleware sırası önemlidir, Authentication/Authorization'dan önce olmalı
- Girdi Doğrulama ve Güvenli Veritabanı Erişimi: Tüm kullanıcı girdilerini, özellikle sorgu parametreleri, gövde verileri ve başlıkları her zaman doğrulayın ve sanitize edin. SQL Injection, XSS (Cross-Site Scripting) ve diğer injection saldırılarını önlemek için güvenli veri erişim yöntemleri (örn. Entity Framework Core, parametreli sorgular, stored procedure'ler) kullanın. Model bağlama (model binding) ve veri ek açıklamaları (data annotations) ASP.NET Core'da bu konuda yardımcı olur.
- Oran Sınırlama (Rate Limiting): API uç noktalarınıza gelen istek sayısını belirli bir zaman diliminde sınırlayın. Bu, kaba kuvvet (brute-force) saldırılarını, DoS (Denial of Service) saldırılarını ve kaynak tüketimini azaltmaya yardımcı olur. `Microsoft.AspNetCore.RateLimiting` paketi veya üçüncü taraf middleware'ler bu işlevi sağlar.
- Güvenlik Başlıkları: HTTP yanıtlarına güvenlik başlıkları (örn. `X-Content-Type-Options`, `X-Frame-Options`, `Content-Security-Policy`, `Strict-Transport-Security`) ekleyerek istemci tarafı saldırılarına ve tarayıcı tabanlı zafiyetlere karşı koruma sağlayın. ASP.NET Core'un kendi `app.UseHsts()` ve `app.UseXfo()` gibi middleware'leri veya `NWebsec.AspNetCore.Middleware` gibi paketler bu konuda yardımcı olur.
- Sır Yönetimi (Secret Management): Veritabanı bağlantı dizeleri, API anahtarları, JWT sırları, şifreleme anahtarları gibi hassas bilgileri asla doğrudan kaynak kodda veya versiyon kontrol sistemlerinde (Git) saklamayın. Geliştirme ortamında User Secrets, üretim ortamında ise Azure Key Vault, AWS Secrets Manager, HashiCorp Vault veya çevre değişkenleri gibi güvenli yöntemler kullanın.
- Kapsamlı Loglama ve İzleme (Logging & Monitoring): Güvenlik olaylarını (başarısız kimlik doğrulamalar, yetkilendirme hataları, anormal istekler, hata mesajları) detaylı bir şekilde loglayın. Bu logları düzenli olarak izleyerek potansiyel güvenlik tehditlerini erken tespit edebilirsiniz. Loglarda asla hassas veri (şifreler, tam tokenlar) bulundurmayın.
- Bağımlılıkları Güncel Tutma: Kullanılan tüm NuGet paketlerini, .NET SDK'sını ve işletim sistemini düzenli olarak güncelleyin. Güvenlik açıklarını kapatan yamalar genellikle güncellemelerle birlikte gelir. Otomatik güvenlik tarama araçlarını (örn. Snyk, Dependabot) kullanmayı düşünün.
- En Az Yetki Prensibi (Principle of Least Privilege): Kullanıcılara ve servislere yalnızca işlerini yapmaları için gereken en az yetkiyi verin. Fazla yetki, bir güvenlik açığı durumunda hasarı artırabilir. Bu ilke hem uygulama içi yetkilendirme hem de altyapı (veritabanı, bulut kaynakları) erişimi için geçerlidir.
- Güvenlik Testleri: Uygulamanızı düzenli olarak güvenlik açıklarını tarama araçlarından (SAST - Static Application Security Testing, DAST - Dynamic Application Security Testing) geçirin ve mümkünse sızma testleri (penetration testing) yaptırın. Güvenlik, sürekli bir süreçtir.
Güvenli API Tasarım Prensipleri
API'larınızı tasarlarken de güvenliği göz önünde bulundurmak kritik önem taşır:
- API Versiyonlama (Versioning): API'larınızın farklı versiyonlarını yönetin. Eski versiyonlardaki potansiyel güvenlik açıklarını kapatmak ve yeni, daha güvenli özellikler sunmak için bu önemlidir. Eski versiyonları kullanımdan kaldırırken dikkatli olun.
- Hata Yönetimi (Error Handling): Hata mesajlarını dikkatli bir şekilde tasarlayın. Detaylı teknik hata mesajlarını üretim ortamında istemcilere göstermekten kaçının; bu, saldırganlara sisteminiz hakkında değerli bilgiler verebilir. Genel ve bilgilendirici hata mesajları döndürün (örn. 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 500 Internal Server Error).
- Idempotent Operasyonlar: Özellikle kritik POST, PUT, DELETE operasyonlarının mümkünse idempotent olmasını sağlayın. Yani aynı isteğin birden çok kez gönderilmesi durumunda sistemde aynı sonucu üretmesini (veya hiçbir yan etki yaratmamasını) sağlayın. Bu, ağ sorunları veya saldırılar sırasında istenmeyen veri bozulmalarını önleyebilir.
Sonuç
ASP.NET Core ile güçlü ve güvenli API'lar geliştirmek mümkündür, ancak bu sürekli dikkat ve en iyi uygulamaların titizlikle uygulanmasını gerektirir. Kimlik doğrulama ve yetkilendirme mekanizmalarını doğru bir şekilde yapılandırmak, JWT'lerin güvenli kullanımını anlamak ve ek güvenlik önlemlerini (HTTPS, CORS, girdi doğrulama, oran sınırlama, sır yönetimi, güvenlik başlıkları) almak, API'larınızın potansiyel saldırılara karşı direncini önemli ölçüde artıracaktır.
Güvenlik, geliştirme sürecinin sonuna bırakılacak bir düşünce değil, tasarım aşamasından itibaren entegre edilmesi gereken bir temel bileşendir. Unutmayın, API'larınız ne kadar güvenli olursa, uygulamanız ve kullanıcılarınızın verileri de o kadar güvende olur. Bu rehberde paylaşılan bilgiler ve stratejiler, ASP.NET Core API'larınız için sağlam bir güvenlik temeli oluşturmanıza yardımcı olacaktır. Güvenli kodlama alışkanlıkları ve sürekli öğrenme ile API'larınızın gelecekteki tehditlere karşı da hazırlıklı olmasını sağlayabilirsiniz. Güvenlik standartlarını takip etmek (örn. OWASP Top 10), düzenli güvenlik denetimleri yapmak ve ekibinizi güvenlik konusunda eğitmek, uzun vadede API'larınızın güvenliğini sağlamanın anahtarıdır.