Kim neyi görebilir? Yetkilendirme modeli: roller, izinler, RBAC ve ABAC
Yetkilendirme, giriş ekranı bittikten sonra başlayan ayrı bir alt sistemdir ve üç soruya birden cevap vermek zorundadır: bu kullanıcı hangi eylemi yapabilir, hangi kayıtlara dokunabilir, o kayıtların hangi alanlarını görebilir. Projelerin çoğu yalnızca ilkini modelliyor. İkincisi genelde altıncı ayda "müdür sadece kendi şubesinin siparişlerini görsün" isteğiyle gelir, bir uç noktaya yazılan tek bir koşulla çözülür, sonra aynı koşul diğer otuz uç noktada eksik kalır. OWASP Top 10 2025'te bir numaralı kategori hâlâ bozuk erişim kontrolü: katkı verisinde 40 CWE'ye eşlenen 1,8 milyondan fazla bulgu ve 32 binin üzerinde CVE var. Bu yazı yetkilendirmeyi bir özellik olarak değil, baştan bir model olarak kurmakla ilgili.
Kimlik doğrulama kim olduğunu söyler, yetkilendirme ne yapabileceğini
Giriş akışı bir kez çalışır ve bir kimlik üretir. Yetkilendirme her istekte yeniden çalışır, çünkü karar isteğin kendisine bağlıdır: aynı kullanıcı 1024 numaralı siparişi görebilir, 1025'i göremez. Bu iki işi aynı kutuya koymak yaygın bir hata, çünkü kimlik sağlayıcısı satın almak yetkilendirmeyi de satın almış olmak anlamına gelmiyor (kimlik doğrulamayı kendiniz mi yazmalısınız ayrı bir karar).
Aynı karışıklık OAuth kapsamlarında da yaşanıyor. Kapsam (scope), bir istemci uygulamanın kullanıcı adına neyi isteyebileceğini sınırlar. orders:read kapsamı taşıyan bir token, o kullanıcının hangi siparişleri okuyabileceğini söylemez. O sorunun cevabı kaynak tarafında, veriye en yakın yerde verilir. Token'da rol taşımak da benzer bir tuzak: rol bilgisi imzalanmış bir jetonun içine girdiği anda, o rol iptal edilse bile token ömrü boyunca geçerli kalmaya devam eder.
Üç ayrı soru: eylem, kayıt, alan
OWASP'ın uygulama güvenliği doğrulama standardı ASVS 5.0.0, 30 Mayıs 2025'te yayımlandı ve yetkilendirmeye ayrı bir bölüm ayırdı. Standardın istediği ayrım pratikte de doğru ayrım:
- Eylem seviyesi: kullanıcı bu işlevi çağırabilir mi? Eksikliğinin adı bozuk fonksiyon seviyesi yetkilendirme, yani normal bir kullanıcının yönetici uç noktasını çağırabilmesi.
- Kayıt seviyesi: çağırabildiği işlevi hangi kayıtlar üzerinde çalıştırabilir? ASVS 8.2.2 bunu doğrudan IDOR ve BOLA'yı engellemek için istiyor.
- Alan seviyesi: aynı kaydın hangi alanlarını okuyabilir, hangilerini yazabilir? ASVS 8.2.3 bunu BOPLA maddesine bağlıyor.
Üçüncüsü en çok atlanan. Destek ekibi müşteri kaydını açabilmeli, ama IBAN alanını görmesi gerekmeyebilir. Daha sinsi olanı yazma tarafı: kullanıcı kendi profilini güncelleyebiliyorsa ve istek gövdesindeki alanlar toptan modele atanıyorsa, isteğe eklenen bir role alanı yetki yükseltmesine dönüşür. API tarafındaki karşılıklarını API güvenliği ve OWASP API Security Top 10 yazısında ayrıntılı ele almıştık.
Rol biriktirmeyin, izin biriktirin
RBAC'in gerçek bir standardı var. ANSI/INCITS 359 ilk olarak 2004'te yayımlandı, 2012'de yeniden basıldı ve 2022'de teyit edildi; NIST'in rol tabanlı erişim kontrolü modeli bu standarda alındı. Standardın kurduğu yapı sade: izinler role bağlanır, kullanıcılar role atanır, rol de bir iş fonksiyonunu temsil eder.
Uygulamada bozulan yer tam burası. Her yeni talep yeni bir rol olarak açılıyor: "depo müdürü", sonra "Ankara depo müdürü", sonra "Ankara depo müdürü (fiyat görmeyen)". Altı ay sonra kimsenin hangi rolün ne yaptığını bilmediği kırk rol oluyor. NIST SP 800-162 bu duruma rol patlaması diyor ve sebebini de net koyuyor: rol atamaları durağandır, dinamik kararları role sığdırmaya çalışmak sınırlı üyeliği olan çok sayıda rol üretir.
Kullanışlı kural şu: rol sayısı iş fonksiyonu sayısı kadar olsun. Kapsam farkı ("hangi şube", "hangi müşteri", "hangi tutara kadar") role değil, verinin kendisine ve kullanıcının niteliklerine yazılsın. Kodun içinde if (user.role === "admin") gibi kontroller yerine if (user.can("order.refund")) gibi izin kontrolleri kalsın; böylece yeni bir rol eklemek kodu değiştirmeyen bir yapılandırma işine döner.
"Sadece kendi şubesini görsün" isteği bir rol değil, bir kapsamdır
Bu istek geldiğinde iki yanlış çözüm var. Birincisi arayüzde filtrelemek. İkincisi veriyi çekip bellekte süzmek, yani veritabanından bin kayıt alıp kullanıcıya elli tanesini göstermek. İkincisi güvenlik açısından çalışır gibi görünür, ama aynı verinin döndüğü ikinci bir uç nokta, bir dışa aktarma özelliği ya da bir rapor yazıldığında filtre orada olmaz.
Doğru yer sorgu. Kullanıcının kapsamı sorgunun where koşulunun parçası olmalı, isteğe bağlı bir katman değil. OWASP'ın A01 için verdiği önerilerden biri bunu tek cümleyle söylüyor: model kayıt sahipliğini zorlamalı, kullanıcının herhangi bir kaydı okuyup değiştirmesine izin veren bir yapı olmamalı. Performans tarafına da dikkat edin: her satır için ayrı bir yetki sorgusu atan bir uygulama, liste ekranlarında hemen N+1 sorgu üretir. Kapsamı tek bir birleştirme ya da tek bir koşula indirebiliyorsanız hem doğru hem hızlı olur.
Veritabanı seviyesinde satır bazlı güvenlik (RLS) bu işin son savunma hattı olabilir, ama tek başına yeterli değil ve varsayılan davranışında bilinmeyen ayrıntılar var. Tabloya RLS açıldığında ve hiç politika tanımlanmadığında PostgreSQL varsayılan olarak hiçbir satırı göstermez, buraya kadar iyi haber. Kötü haber: tablo sahibi politikaları normalde atlar, FORCE ROW LEVEL SECURITY verilmediyse uygulamanız tabloyu sahibiyle bağlanıyorsa politikalar sessizce devre dışı kalır. Ayrıntısını çok kiracılı SaaS mimarisi yazısında ele aldık.
RBAC yetmediğinde: nitelik ve ilişki
Bazı kararlar role sığmaz. "Kendi ekibinin masraf formunu onaylayabilir, ama 50 bin liranın üstünü onaylayamaz, mesai dışında hiç onaylayamaz" cümlesinde üç farklı nitelik var. NIST SP 800-162'nin tanımı bu tür kararlar için: ABAC, öznenin, nesnenin, istenen işlemin ve bazı durumlarda çevre koşullarının niteliklerini bir politikaya karşı değerlendirir. Kazandığınız şey esneklik, ödediğiniz bedel nitelik ve politika yönetimi. Nitelikler yanlışsa kararlar da yanlış olur, yani "hangi kullanıcı hangi şubeye bağlı" verisinin güncel tutulması artık bir güvenlik işi.
Üçüncü model ilişki üzerine kurulu. "Bu klasörün sahibiyim, dolayısıyla içindeki dosyaları paylaşabilirim" ya da "bu şubenin müdürüyüm, dolayısıyla altındaki bütün siparişleri görürüm" cümlelerinde karar bir ilişki grafiğinde yürüyor. Bu yaklaşımın referans işi Google'ın 2019'da USENIX ATC'de yayımladığı Zanzibar bildirisi: tek bir yetkilendirme sistemi trilyonlarca erişim kontrol kaydını tutuyor, saniyede milyonlarca sorguya cevap veriyor, üç yıllık üretim kullanımında 95. yüzdelik gecikmeyi 10 milisaniyenin altında ve erişilebilirliği %99,999'un üzerinde tutmuş. Bugün aynı modeli izleyen açık kaynak projeler var: OpenFGA CNCF'te Kasım 2025'te incubating seviyesine çıktı, SpiceDB aynı bildiriden türedi, politika tarafında OPA ve Cerbos benzer bir iş yapıyor.
Karar ölçütü şu: yetki kuralları yayına çıkma sıklığınızdan daha hızlı değişiyorsa ya da müşterileriniz kendi rollerini kendileri tanımlıyorsa ayrı bir yetki motoru mantıklı. Bunların ikisi de yoksa, kod içinde tek bir yetki modülü çoğu ürün için fazlasıyla yeterli. Yeni bir altyapı bileşeni eklemek, kuralların karmaşıklığını azaltmaz, sadece yerini değiştirir.
Kararı tek yerde verin, her istekte uygulayın
Sağlıklı kurulumun iki parçası var: kararı veren yer (politika karar noktası) ve kararı uygulayan yer (politika uygulama noktası). NIST'in ABAC rehberi bu ayrımı kurar, OWASP da A01 için aynı şeyi öneriyor: mekanizmayı bir kez yazın, uygulamanın her yerinde tekrar kullanın. Pratikte bu, yetki kararının serpiştirilmiş koşullarda değil, çağrılabilir tek bir arayüzde durması demek.
Yol seviyesindeki ara katman (middleware) bu işin yarısıdır. /admin altındaki her şeyi yönetici rolüne kapatmak eylem seviyesini çözer, kayıt seviyesini çözmez. Kayıt kontrolü veriye erişen katmanda, nesne elde edildikten hemen sonra yapılmak zorunda. Varsayılan da reddetmek olsun: yeni yazılan uç nokta hiçbir izinle eşleşmiyorsa erişilebilir değil, kapalı olmalı. OWASP'ın ifadesi kısa, "genel erişime açık kaynaklar dışında varsayılan olarak reddet". Bir de açık reddin her zaman kazandığı bir kural yazın; iki politika çakıştığında hangisinin geçerli olduğu tartışma konusu olmamalı.
Arayüzde butonu saklamak yetkilendirme değildir
Bu cümle bariz görünür, ama pratikte en sık yapılan hata bunun bir varyantı. Butonu kaldırmak, menüyü gizlemek, sayfa yönlendirmesi koymak istemci tarafında olan işlerdir ve istemci saldırganın elindedir. OWASP'ın ifadesi net: erişim kontrolü ancak saldırganın kontrolü ya da meta veriyi değiştiremeyeceği güvenilir sunucu tarafı kodda etkilidir. Kimliği tahmin edilemez yapmak (artan sayılar yerine UUID kullanmak) keşfi zorlaştırır, yetki kontrolünün yerine geçmez.
CISA, NSA ve Avustralya ACSC'nin 27 Temmuz 2023'te yayımladığı ortak bülten (AA23-208A) tam bu konu üzerine. Bültenin tespiti IDOR açıkları için üç maddede özetleniyor: yaygın, geliştirme süreci dışında önlenmesi zor ve ölçekli istismara açık. Önerisi de tek satır: hassas veriye erişen, onu değiştiren ya da silen her istekte kimlik ve yetki kontrolü yapılmalı.
Bir haneyi değiştirmek: 885 milyon belge
Bu açığın maliyetini en iyi gösteren örnek First American Financial. Mayıs 2019'da ortaya çıktı: şirket müşterilerine işlem belgelerine ulaşmaları için bağlantı gönderiyordu ve bağlantıdaki numarayı bir hane değiştiren herkes başka bir işlemin belgelerini görebiliyordu. 2003'e kadar geri giden yaklaşık 885 milyon kayıt bu şekilde açıktaydı; içeriğinde banka hesap numaraları, ipotek ve vergi kayıtları, sosyal güvenlik numaraları ve ehliyet görüntüleri vardı. Şirket açığı 2018'de fark etmiş, ama sorun aylarca giderilmeden kalmıştı.
New York eyaleti finansal düzenleyicisi NYDFS, 28 Kasım 2023'te 1 milyon dolarlık uzlaşmayı duyurdu; ihlal edilen maddeler arasında erişim kontrolleri ve kimlik yönetimi ile risk değerlendirmesi sayıldı. Buradaki teknik ders küçük, yönetsel ders büyük: kimlik doğrulama vardı, oturum vardı, şifreleme vardı. Eksik olan tek şey "bu belge bu kullanıcıya mı ait" sorusuydu ve o soru hiçbir yerde sorulmuyordu.
Vekâlet, destek girişi ve servisler arası çağrı
ASVS'nin 8.3.3 maddesi şunu istiyor: bir nesneye erişim, kullanıcı adına hareket eden aracının değil, isteği başlatan öznenin izinlerine dayanmalı. Bu madde iki yaygın deseni doğrudan hedefliyor. Birincisi destek ekibinin "kullanıcı olarak giriş yap" özelliği. Bu özellik gerekli, ama süreli olmalı, gerekçe istemeli, log kaydında hem gerçek kullanıcıyı hem taklit edilen kullanıcıyı tutmalı ve taklit oturumunda yıkıcı işlemler kapalı olmalı.
İkincisi servisler arası çağrılar. İç servisleriniz birbirini "her şeyi yapabilen" bir anahtarla çağırıyorsa, o servisin arkasındaki bütün kontroller anlamsızlaşır: dışarıdan gelen istek bir kez o servise ulaştığında, servis kullanıcının izinlerini değil kendi izinlerini kullanır. Klasik adı karışık vekil (confused deputy) problemi. Çözüm, kullanıcı bağlamını çağrı zinciri boyunca taşımak ve son kararı veriye en yakın serviste vermek.
İptal ne kadar sürede geçerli olur
Yetki kararları her yerde önbelleğe alınır: token içine gömülen roller, karar önbellekleri, okuma kopyalarının gecikmesi. Bunun sonucu şu: bir yetkiyi kaldırdığınızda etkisi anında görünmez. Zanzibar bildirisi bu soruna bir isim koyuyor, "yeni düşman" problemi. Erişim kontrol listesi güncellemeleri ile içerik güncellemeleri arasındaki nedensel sıra korunmazsa iki kötü sonuç doğar: eski liste yeni içeriğe uygulanır ya da kaldırılmış bir erişim yeniden geçerli olur. Google bunu her güncellemeye zaman damgası atayarak ve istemciye "zookie" denen bir tutarlılık jetonu vererek çözüyor.
Sizin ölçeğinizde çözüm daha basit ama karar yine sizin: token ömrünü kısa tutun, rol değişiminde oturumları geçersiz kılın, para hareketi ve veri silme gibi işlemlerde kararı önbellekten değil kaynağından okuyun. Asıl yapılacak iş bir sayı yazmak: yetki kaldırıldığında en kötü durumda kaç saniye sonra geçerli olacak? Bu sayının yazılı bir cevabı yoksa, iptal diye bir şeyiniz de yok.
Test edilmeyen yetki, olmayan yetkidir
Yetkilendirme, otomatik tarayıcıların en zayıf olduğu alan, çünkü tarayıcı kimin neyi görmesi gerektiğini bilmiyor. CISA bülteninin "geliştirme süreci dışında önlenmesi zor" tespiti tam olarak bunu anlatıyor. Yani bu iş sizin test setinize düşüyor.
Çalışan desen bir matris testi: en az iki kiracı, her kiracıda en az iki rol ve her yetki gerektiren uç nokta için "yetkisiz kullanıcı bunu çağırırsa ne olur" beklentisi. Testin doğruladığı şey 200 dönmemesi değil, doğru şekilde reddedilmesi: başkasının kaydına erişimde 404, kendi kapsamında olup izni olmayan işlemde 403. Bu testler test otomasyonu piramidinizin en ucuz katmanına yazılabilir ve yeni uç nokta eklendiğinde bir tanesinin eksik olduğunu derleme değil, sizin listeniz yakalar. Loglama da tarafını tamamlıyor: OWASP erişim kontrolü hatalarının kaydedilmesini ve gerektiğinde uyarı üretilmesini öneriyor. Arka arkaya 403 alan bir hesap, kaza yapan bir kullanıcı değil, kapıları deneyen bir hesaptır (log yönetimi ve izleme burada devreye girer).
Bu haftaya sığan ilk adım
Beş soruyla başlayın. Bir: yetki kuralları yazılı mı, yoksa yalnızca kodun içinde mi duruyor? Rolleri ve izinleri satır satır listeleyen tek bir tablo, çoğu ekipte yarım günlük iş ve ilk çelişkiyi hemen gösterir. İki: kayıt seviyesindeki kontrol nerede yapılıyor, sorguda mı bellekte mi? Üç: kaç roliniz var ve bunların kaçı gerçek bir iş fonksiyonu, kaçı tek bir müşteri isteğinden doğmuş bir kopya? Dört: destek ekibi bir kullanıcının hesabına girebiliyor mu, giriyorsa bu kayıt altında mı? Beş: bir kullanıcının yetkisini kaldırdığınızda en geç kaç saniye içinde etkisini görüyorsunuz?
Bu beşinin dördü tek oturumda cevaplanır. Cevaplar çıktıktan sonra sıra da kendini belli eder: önce kayıt seviyesindeki kontrolü sorguya indirin, sonra rolleri iş fonksiyonlarına sadeleştirin, en son yetki motoru gibi altyapı kararlarını konuşun. Bu sırayı tersine çevirmek, yani modeli düzeltmeden araç seçmek, aynı karmaşıklığı daha pahalı bir yerde tekrar kurmak anlamına geliyor.
Bu konuda yardıma mı ihtiyacınız var?