CORS, rate limiting ve güvenlik başlıkları: API'yi dış tehditlere karşı sağlamlaştırmak
API'yi kötüye kullanıma karşı sağlamlaştıran üç konuyu işliyoruz: CORS ile hangi sitelerin erişebileceğini kontrol etmek, rate limiting ile isteği sınırlamak ve güvenlik başlıklarıyla temel korumayı eklemek.
Kimlik ve yetki artık yerinde: kullanıcıyı tanıyoruz, kimin neye erişebileceğini kontrol ediyoruz. Ama güvenlik sadece "kim girebilir?" sorusundan ibaret değil. API'n dış dünyaya açık bir kapı ve bu kapıyı kötüye kullanmak isteyenler olacak. Bugün güvenlik bloğunu kapatırken, API'yi bu tür tehditlere karşı sağlamlaştıran üç önemli konuyu ele alıyoruz: CORS, rate limiting ve güvenlik başlıkları.
CORS: Hangi Siteler API'ne Erişebilir?
CORS, "Cross-Origin Resource Sharing" demek. Kulağa karmaşık geliyor ama çözdüğü sorun basit: bir web tarayıcısında çalışan bir sitenin, başka bir adresteki API'ye istek atıp atamayacağını kontrol etmek. Tarayıcılar, güvenlik gereği, bir sitenin farklı bir kaynağa (origin) istek atmasını varsayılan olarak engeller. CORS, bu engeli bilinçli biçimde gevşetmenin yoludur.
Bunu bir binanın ziyaretçi listesi gibi düşün. Güvenlik görevlisi herkesi içeri almaz; sadece listedeki isimleri kabul eder. CORS de böyle çalışır: API'ne "şu sitelerden gelen isteklere izin ver, gerisini reddet" dersin. Böylece senin frontend'in API'ne erişebilirken, tanımadığın bir site aynı isteği tarayıcıdan yapamaz.
.NET'te CORS'u önce servis olarak kaydeder, sonra bir politika tanımlarsın:
builder.Services.AddCors(options =>
{
options.AddPolicy("Frontend", policy =>
{
policy.WithOrigins("https://benimsitem.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
Bu politika, yalnızca https://benimsitem.com adresinden gelen isteklere izin verir. Sonra bunu boru hattına eklersin:
app.UseCors("Frontend");
Önemli bir uyarı: bazen örneklerde her yere izin veren AllowAnyOrigin görürsün. Öğrenirken pratik olsa da, gerçek projede bunu kullanmak güvenlik açığıdır; kapıyı herkese açmak demektir. Her zaman izin verilen adresleri açıkça belirt. Bir de şunu netleştirelim: CORS bir tarayıcı kuralıdır, bir kimlik doğrulama mekanizması değil. Yani CORS API'ni tarayıcı tabanlı istismardan korur ama tek başına yeterli bir güvenlik katmanı değildir; kimlik doğrulamanın yerini tutmaz.
Rate Limiting: İsteği Sınırlamak
API'n açık bir kapıysa, birileri o kapıyı defalarca, saniyede yüzlerce kez çalabilir. Belki kötü niyetli bir saldırı, belki hatalı bir uygulama döngüde takılmış. Her iki durumda da sunucun boğulur ve gerçek kullanıcılar hizmet alamaz. Rate limiting, bir kullanıcının belirli bir sürede kaç istek atabileceğini sınırlayarak bu tehdide karşı koyar.
Bunu bir gişedeki turnike gibi düşün. Turnike, belirli bir sürede belirli sayıda kişinin geçmesine izin verir; kimse kapıyı zorlayıp akını başlatamaz. Rate limiting de API'nin önünde böyle bir turnike kurar: "dakikada en fazla yüz istek" gibi bir sınır koyarsın, bu sınırı aşan istekler geçici olarak reddedilir.
.NET, rate limiting'i yerleşik olarak destekler. Basit bir sabit pencere (fixed window) sınırı şöyle kurulur:
builder.Services.AddRateLimiter(options =>
{
options.AddFixedWindowLimiter("Sabit", limiterOptions =>
{
limiterOptions.PermitLimit = 100;
limiterOptions.Window = TimeSpan.FromMinutes(1);
});
});
Bu ayar, bir dakikalık pencerede en fazla yüz isteğe izin verir. Sınır aşıldığında, API otomatik olarak 429 Too Many Requests durum kodunu döner; bu kod, "çok fazla istek attın, biraz yavaşla" demenin standart yoludur. Sonra bu sınırlayıcıyı boru hattına eklersin:
app.UseRateLimiter();
Farklı sınırlama stratejileri vardır (sabit pencere, kayan pencere, token kovası gibi) ve ihtiyacına göre seçersin. Ama temel fikir hepsinde aynı: hiçbir tek kullanıcının, sistemin tamamını tüketmesine izin verme.
Güvenlik Başlıkları: Küçük Ama Etkili Koruma
Her HTTP cevabı, gövdesinin yanında başlıklar (header) taşır. Bu başlıklardan bazıları, tarayıcıya "şu güvenlik önlemlerini uygula" diye talimat verir. Doğru güvenlik başlıklarını eklemek, birçok yaygın saldırıya karşı ucuz ve etkili bir savunma sağlar. Birkaç önemlisi şunlar:
X-Content-Type-Options, tarayıcının bir dosyanın türünü tahmin etmeye çalışmasını engeller; bu, bazı sinsi saldırıları kapatır. X-Frame-Options, sitenin başka bir sayfanın içine gizlice gömülmesini (clickjacking denen saldırı) önler. Strict-Transport-Security ise tarayıcıya "bu siteyle her zaman güvenli bağlantı kur" der.
Bu başlıkları eklemenin basit bir yolu, küçük bir middleware yazmaktır:
app.Use(async (context, next) =>
{
context.Response.Headers["X-Content-Type-Options"] = "nosniff";
context.Response.Headers["X-Frame-Options"] = "DENY";
await next();
});
Bu middleware, her cevaba bu güvenlik başlıklarını ekler. On üçüncü yazıda öğrendiğimiz middleware mantığını hatırla: bu kod, her isteğin geçtiği bir kapı gibi çalışır ve çıkışta cevaba başlıkları iliştirir. Küçük bir ekleme ama savunma derinliğine ciddi katkı sağlar.
Güvenlik Tek Bir Şey Değil, Katmanlardır
Bugünkü üç konunun ortak bir dersi var: güvenlik tek bir önlemle sağlanmaz, katmanlardan oluşur. Kimlik doğrulama bir katman, yetkilendirme başka bir katman; CORS, rate limiting ve güvenlik başlıkları da ayrı katmanlar. Hiçbiri tek başına yeterli değildir, ama üst üste geldiklerinde güçlü bir savunma oluştururlar. Buna genellikle "derinlemesine savunma" denir: bir katman aşılsa bile, arkasında başka katmanlar bekler.
Bu yüzden "JWT ekledim, güvenlik tamam" diye düşünmek tehlikelidir. JWT sadece kimliği çözer. Kötüye kullanım, aşırı yüklenme, tarayıcı tabanlı saldırılar hâlâ ayrı önlemler ister. İyi bir geliştirici, güvenliği tek bir kutu değil, birbirini tamamlayan önlemler bütünü olarak düşünür.
Küçük Bir Deneme
Projene önce CORS ekle ve yalnızca belirli bir adrese izin ver. Sonra farklı bir origin'den (mesela başka bir port'ta çalışan basit bir sayfadan) API'ne tarayıcı üzerinden istek atmayı dene; tarayıcının bu isteği nasıl engellediğini gör. Ardından rate limiting'i kur, sınırı bilerek düşük tut (mesela dakikada beş istek) ve aynı endpoint'e art arda istek atarak 429 cevabını kendi gözünle gözlemle. Son olarak güvenlik başlıkları middleware'ini ekle ve tarayıcının geliştirici araçlarından cevaba eklenen başlıkları incele. Bu üç deney, soyut güvenlik kavramlarını elle tutulur hale getirir.
Bu yazıyla güvenlik ve kimlik bloğunu tamamladık. Bir sonraki yazıda kalite ve dağıtım bloğuna geçiyoruz. İlk konu, API'ni başkalarının nasıl kullanacağını anlatan dokümantasyon olacak: Swagger/OpenAPI ve Scalar ile API'ne otomatik, interaktif bir kılavuz kazandırmayı konuşacağız. Artık API'n güvenli; sırada onu anlaşılır ve kullanılabilir kılmak var.