Bir API uç noktası oturum açmış kullanıcıya açık olduğunda ilk güvenlik eşiği geçilmiş gibi hissedilebilir. Ancak doğrulanmış bir kimlik, kullanıcının her kaynağı görmeye veya her işlemi yapmaya yetkili olduğu anlamına gelmez. “Kim bu?” sorusu ile “Bu kişi bu kaynağa, bu eylem için erişebilir mi?” sorusu farklıdır. Birincisi kimlik doğrulama, ikincisi yetkilendirmedir.
Bu ayrım özellikle .NET servisleri büyüdükçe önem kazanır. Başlangıçta controller içinde birkaç rol kontrolü yeterli görünebilir. Zamanla tenant üyeliği, sahiplik, kayıt durumu, abonelik planı, onay akışı ve yönetici istisnaları eklenir. Bu kurallar uç noktalara dağılırsa davranış tutarsızlaşır; bir yeni endpoint aynı korumayı unutabilir. Amaç yalnızca erişimi reddetmek değil, kararın hangi kurala dayandığını açık, tekrar kullanılabilir ve test edilebilir hâle getirmektir.
Kimlik doğrulama ve yetkilendirme sonucunu ayırın
Geçerli erişim belirteci olmayan veya geçersiz kimlik taşıyan istek için yanıt genellikle 401 Unauthorized olur. İstemci kim olduğunu kanıtlayamaz. Kimliği doğrulanmış kullanıcı ise gerekli izne veya kaynağa erişim hakkına sahip değilse 403 Forbidden daha doğru sonuçtur. Bu ayrım istemci akışları, loglar ve güvenlik incelemesi için anlamlıdır.
Kimlik doğrulama katmanının sorumluluğu, belirtecin imzasını, süresini, hedef kitle ve veren bilgisini doğrulamak; ardından güvenilir bir ClaimsPrincipal üretmektir. Yetkilendirme katmanı ise bu kimlik ve uygulama bağlamını kullanarak işlem iznini değerlendirir. Token içinde bir rol göründüğü için doğrudan işlem yapmak, özellikle üyelik veya hesap durumu değişebilen sistemlerde yetersiz kalabilir.
Kısa ömürlü token, yenileme akışı ve oturum sonlandırma stratejisi de tasarımın parçasıdır. Yetki değişikliği anında etkili olmak zorundaysa yalnızca token süresine güvenmek yerine, kritik işlemlerde güncel üyelik veya durum kontrolü gerekebilir.
Rol kontrollerini iş kuralına dönüştürün
[Authorize(Roles = "Admin")] basit ve bazı durumlarda uygundur. Fakat “raporu dışa aktarabilir”, “başkasının taslağını onaylayabilir” veya “bu tenant'ın fatura ayarını değiştirebilir” gibi kurallar sadece rol adı değildir. Eylem, kaynak ve bağlam içerir.
.NET policy tabanlı yetkilendirme, bu kuralı isimli bir gereksinim olarak merkezileştirmeye yardım eder. Örneğin BillingSettingsWrite veya ReportExport politikası, ilgili claim, rol veya özel requirement'ları bir araya getirir. Endpoint yalnızca niyeti belirtir:
[Authorize(Policy = "ReportExport")]
[HttpPost("reports/{id}/export")]
public async Task<IActionResult> Export(string id) { /* ... */ }
Bu yaklaşım policy'nin her yerde aynı tanımla uygulanmasını, test edilmesini ve dokümante edilmesini kolaylaştırır. Politika adı teknik izin değil, mümkünse ürün dilindeki yeteneği ifade etmelidir. CanDoThing2 yerine InvoiceApprove gibi bir ad, kod incelemesinde kararın anlamını görünür kılar.
Kaynak tabanlı kararı veri yüklendikten sonra verin
Bir kullanıcının rapor dışa aktarma yetkisine sahip olması, her raporu dışa aktarabileceği anlamına gelmeyebilir. Rapor başka bir tenant'a ait olabilir, kilitli olabilir veya yalnızca sahibi tarafından görülebilir. Bu noktada kaynak tabanlı yetkilendirme gerekir.
Sağlam akış şudur: Kaynağı, erişen kişinin tenant bağlamıyla birlikte güvenli biçimde yükleyin; sonra kullanıcı, kaynak ve gereksinimi bir authorization handler'a verin. Handler sahiplik, üyelik, kaynak durumu veya iş kuralını değerlendirir. Kaynak bulunamadı ile erişim yok bilgisinin dışarı nasıl verileceği de ürün ve güvenlik kararına göre tutarlı olmalıdır.
Önemli olan, kaynağı önce herkese açık sorguyla getirip sonra controller'da filtrelemek değildir. Veri erişim katmanı da tenant ve erişim sınırını varsayılan olarak uygular. Yetkilendirme, yanlış sorgudan sonra gelen tek bir kontrol değil; sorgunun tasarımıyla başlayan bir savunmadır.
Claim'leri gerçek zamanlı veritabanı yerine kullanmayın
Claim'ler kimlikte taşınması yararlı olan nispeten kararlı bilgiler için uygundur: kullanıcı kimliği, doğrulanmış e-posta durumu, kimlik sağlayıcısı veya genel rol. Çok sayıda dinamik yetkiyi, kaynak listelerini ya da büyük tenant üyeliği verisini token'a koymak ise token'ı şişirir ve veriyi hızla eskitebilir.
“Bu kullanıcı bu projenin editörü mü?” gibi kaynakla değişen sorular, gerektiğinde uygulama verisinden yanıtlanmalıdır. Bu kontrolü her endpoint'te rastgele SQL yazmak yerine bir authorization handler veya uygulama servisi arkasında toplamak daha güvenlidir. Gerekirse kısa süreli ve dikkatle anahtarlanmış önbellek kullanılabilir; fakat iptal ve yetki değişiminde nasıl geçersiz kılınacağı baştan planlanmalıdır.
Sunucu tarafındaki her giriş noktasını koruyun
Bir UI düğmesini gizlemek kullanıcı deneyimine yardımcı olur, fakat güvenlik sınırı değildir. İstemci isteği elle oluşturabilir, eski mobil sürüm farklı endpoint çağırabilir ya da arka plan işi işlemi başlatabilir. Bu nedenle HTTP endpoint, GraphQL resolver, mesaj tüketicisi, background worker ve yönetim komutu kendi yetkilendirme bağlamını doğrulamalıdır.
Servisler arası çağrılarda da “iç ağdan geliyor” varsayımı yeterli değildir. Çağıran servis kimliği, hedef kitle, minimum kapsam ve çağrının kullanıcı adına mı yoksa sistem adına mı yapıldığı açık olmalıdır. Kullanıcı bağlamı taşınıyorsa hangi claim'lerin güvenilir olduğu ve kimin imzaladığı doğrulanmalıdır.
Ayrıcalıklı yönetim işlemlerinde güçlü doğrulama, dar kapsam, zaman sınırlaması ve audit kaydı düşünülmelidir. Yönetici rolü, tüm veri sınırlarını sessizce aşan genel geçer bir anahtar olmamalıdır.
Kararları denetlenebilir ama mahrem tutun
Yetkilendirme hatasını teşhis edebilmek için hangi policy'nin, hangi kaynak türünde ve hangi sonuçla değerlendirildiği loglanabilir. Ancak access token, tam claim dökümü, kişisel içerik veya hassas kaynak bilgisi loglara yazılmamalıdır. Bir request veya correlation ID, destek ekibinin olayı izleyebilmesi için çoğu zaman yeterlidir.
Denetim kaydı daha kalıcı bir soruyu yanıtlar: Kim, ne zaman, hangi kaynağa, hangi eylem için erişmeye çalıştı ve karar neydi? Her okuma isteğini ayrıntılı kaydetmek pahalı veya mahremiyet açısından gereksiz olabilir. Yüksek riskli yazma, dışa aktarma, rol değişimi ve yönetim eylemlerini önceliklendirmek daha dengeli bir yaklaşımdır.
Yetkilendirmeyi olumsuz senaryolarla test edin
Sadece yetkili kullanıcının başarılı olduğu test, güvenlik kuralını kanıtlamaz. Her kritik özellik için kimliği olmayan, yanlış roldeki, başka tenant'taki, kaynağın sahibi olmayan ve süresi geçmiş bağlamdaki kullanıcıları test etmek gerekir. 401 ile 403 ayrımı, kaynak tabanlı karar, arka plan işi ve yönetici istisnası bu senaryolarda kapsanmalıdır.
Policy handler'ları birim testle, endpoint ve veri erişimi birleşimi ise entegrasyon testiyle doğrulamak iyi bir ayrımdır. Yeni endpoint eklendiğinde yetkilendirme gereksinimi kod inceleme kontrol listesinde yer almalıdır.
İyi yetkilendirme, her controller'a daha fazla if yazmak değildir. Kimliği güvenilir biçimde doğrulayan, iş yeteneklerini isimli policy'lerle ifade eden, kaynak ve veri sınırını birlikte değerlendiren sistemler; hem daha az ayrıcalık verir hem de değişen ürün kurallarına daha güvenle uyum sağlar.