İçeriğe atla
0 XP 0
12 dkderinleşmeyle 13 dk50 XP6 etkinlik

Nesne ve fonksiyon düzeyinde yetkilendirme

Bu derste
  • API'de BOLA kaydın sahibini, BFLA ise işlemin kime açık olduğunu sormayı unutmaktır; ikisi de kimliği karara katmamaktan doğar.
  • OWASP API Security Top 10 2023 listesini, JWT'nin rolü nasıl taşıdığını ve açığın istekle yanıttaki izini göreceksin.
  • Merkezi rol denetimi, sorgu içi sahiplik filtresi ve dar kapsamlı servis token'ıyla iki açığı sunucuda kapatacaksın.

Puan bütün etkinlikleri cevaplayınca hesaba yazılır. Bu dersi tamamlayınca +50 XP alacaksın. Giriş yapmadan ilerlersen puan yalnız bu tarayıcıda durur.

Altay Kargo'nun mobil uygulaması ekranda gördüğün her şeyi bir API'den çeker. Sipariş listesi, teslimat rotası ve iade talebi aynı yoldan gelir. Uygulama bir istek gönderir; istek bir kayıt numarası ya da bir işlem adı taşır.

Sunucu bu bilgiye bakıp veri döner ya da işlemi çalıştırır. Her istekte iki soru sorması gerekir: bu kayıt kimin, bu işlem kime açık.

Birinci soru atlanırsa nesne düzeyi yetki hatası doğar. İkincisi atlanırsa fonksiyon düzeyi yetki hatası doğar. IDOR'u bir web sayfasında gördüysen mantık tanıdık gelecek. API'de fark şudur: neredeyse her uç bir kimlik numarası taşır ve arayüz olmadan doğrudan çağrılabilir.

API'lerin on büyük riski

OWASP, API'lere özgü riskleri ayrı bir listede toplar. Güncel sürüm 2023 tarihlidir ve on madde taşır. API1 nesne düzeyi yetki hatasıdır. API2 bozuk kimlik doğrulamayı, API3 nesne özelliği düzeyi yetki hatasını anlatır.

API4 kısıtsız kaynak tüketimi, API5 fonksiyon düzeyi yetki hatasıdır. API6 hassas iş akışlarına sınırsız erişimi kapsar; kampanya kodunu bir betikle toplamak ya da bilet stoğunu otomatik kapatmak bu maddeye girer. API7 sunucu taraflı istek sahteciliği (SSRF), API8 güvenlik yapılandırma hatasıdır. API9 eksik envanter yönetimini, API10 başka bir API'den gelen veriye sorgusuz güvenmeyi anlatır.

Listenin ilk sırası değişmedi: nesne düzeyi yetki hatası 2019 sürümünde de birinciydi. Sıradaki dersler API3, API4, API8 ve API9'u açar. Burada API1 ile API5 üstünde durursun.

Nesne düzeyi yetki hatasının İngilizce adı Broken Object Level Authorization, kısaltması BOLA'dır. Hata, sunucunun kaydı yalnız istekteki numaraya göre çekmesinden doğar. İsteği kimin gönderdiği sorguya hiç girmez.

Web dünyasında aynı hataya IDOR denir. API dünyasında ayrı bir ad almasının nedeni yaygınlığıdır: siparişler, faturalar, mesajlar ve belgeler hep bir kimlik numarasıyla çağrılır. Kontrol basit görünür, ama her yeni uçta yeniden yazılması gerekir.

Bir mobil uygulamanın arka ucu, bir iş ortağı entegrasyonu ya da bir iç yönetim API'si aynı hataya aynı kolaylıkla düşer. Saldırganın ihtiyacı yalnız kendi geçerli hesabıdır. Kendi kaydını isterken gördüğü numarayı değiştirir ve başkasının kaydını ister.

Fonksiyon düzeyi yetki hatası (Broken Function Level Authorization, BFLA) listede beşinci sıradadır. Burada sorun kaydın sahibi değil, ucun kendisidir. Sıradan bir kullanıcı, yalnız yöneticiye ait bir uca doğrudan istek gönderir.

Sunucu rolü denetlemeden işlemi çalıştırırsa hata doğar. BOLA "bu kayıt senin mi" sorusunu, BFLA "bu işlem sana açık mı" sorusunu atlar. İkisi de aynı kök nedene dayanır: sunucu isteği gönderenin kim olduğunu bilir ama bu bilgiyi karar noktasında kullanmaz.

Yönetici uçlarını tahmin etmek zor değildir. /api/kullanicilar varsa /api/admin/kullanicilar denenir, GET kabul ediliyorsa DELETE denenir. Belgesi olmayan, test için bırakılmış bir uç bu hatayı özellikle iyi saklar, çünkü kimse onu aramaz.

Etkinlik 1 / 6

Aşağıdaki API olaylarını BOLA ya da BFLA olarak ayır.

Önce bir seçeneğe, sonra aşağıdaki uygun kutuya bas. İstersen seçeneği kutuya sürükleyip bırak. Yerini değiştirmek için aynı işlemi tekrarla.

0/4 seçenek yerleşti.

Token rolü nasıl taşır

Sunucu isteği kimin gönderdiğini çoğu zaman bir token'dan öğrenir. API'lerde en yaygın biçim JSON Web Token'dır (JWT) ve RFC 7519'da tanımlanır. Bir JWT noktayla ayrılmış üç parçadır. Başlık (header) imza algoritmasını ve token tipini söyler.

Payload, kullanıcıya dair iddiaları (claim) taşır. sub kullanıcı kimliğini, exp son geçerlilik zamanını gösterir. rol gibi özel bir alan da yetkiyi taşır. İmza, ilk iki parçanın sunucunun anahtarıyla üretildiğini kanıtlar.

Parçalar Base64URL ile kodlanır, şifrelenmez. Token'ı eline alan herkes payload'ı okuyabilir. Okumak değiştirmek demek değildir. Payload'da tek bir karakter değişirse imza tutmaz ve sunucu token'ı reddeder.

// 1. parça: başlık
{ "alg": "RS256", "typ": "JWT" }

// 2. parça: payload
{ "sub": "4410", "rol": "musteri", "iss": "https://kimlik.altaykargo.example", "exp": 1830000000 }

// 3. parça: imza (ham baytlar, okunur metin değil)
Altay Kargo'nun mobil uygulamasına verilen token'ın çözülmüş hali. Rol, imzanın koruduğu payload'da durur.

Token'ı çözmek ile doğrulamak ayrı işlerdir. Çözmek yalnız Base64URL kodlamasını açar. Doğrulamak imzayı anahtarla yeniden hesaplar ve gelen imzayla karşılaştırır.

JWT için en iyi uygulamaları toplayan RFC 8725, kabul edilecek algoritmaları çağıran kodun sabitlemesini ister. Başlıktaki alg alanı saldırganın elindedir. Bazı eski kütüphaneler alg değeri none olan, yani imzasız bir token'ı geçerli saymıştır.

Çare açıktır: sunucu beklediği algoritmayı yapılandırmada yazar, başlıktan okumaz. Ardından token'ı kimin verdiğini (iss), kime verildiğini (aud) ve süresini (exp) denetler. Rol kararı yalnız bu adımlardan geçmiş bir payload'a dayanır. Yalnız çözen bir fonksiyonun sonucuna göre yetki vermek, imzasız bir notu kimlik kartı saymaktır.

Derinleş:Algoritma karışıklığı neden çalışır

Asimetrik imzada (RS256) sunucu token'ı gizli anahtarla imzalar, herkes açık anahtarla doğrular. Simetrik imzada (HS256) ise aynı gizli değer hem imzalar hem doğrular. Doğrulama kodu algoritmayı başlıktan okuyan bir kütüphaneye yalnız "şu anahtarla doğrula" derse sorun başlar.

Saldırgan başlığa HS256 yazar ve token'ı, herkesin erişebildiği açık anahtarı gizli değer gibi kullanarak imzalar. Kütüphane başlığa uyar, açık anahtarı HMAC anahtarı sayar ve imza tutar. Böylece saldırgan istediği rolü taşıyan bir token üretmiş olur.

RFC 8725 bu yüzden kütüphanelerin çağıranın verdiği algoritma listesi dışında hiçbir algoritmayı kullanmamasını şart koşar. Güncel kütüphanelerin çoğu anahtar türünü algoritmaya bağlar ve RSA anahtarını HMAC doğrulamasında reddeder. Eski bir bağımlılık ya da elle yazılmış doğrulama kodu bu korumayı taşımayabilir. Kod incelemesinde doğrulama çağrısına algoritma listesinin açıkça verilip verilmediğine bak.

Etkinlik 2 / 6

BFLA denetimi, kullanıcının rolünü JWT'nin hangi bölümünden okuyarak karar verir?

Diyagramda doğru kutuyu tıkla.

Headeralg, typPayloadsub, exp, rolİmzagizli anahtarÜç parça nokta ile birleşir ve Base64URL ile kodlanır. Kodlama şifreleme değildir.

Çözümlü örnek: iki uç, iki hata

// Güvensiz: rol de sahiplik de denetlenmiyor
app.get("/api/siparisler/:id", oturumGerekli, async (req, res) => {
  const s = await db.siparis.findUnique({ where: { id: Number(req.params.id) } });
  res.json(s);
});
app.post("/api/admin/iade-onayla/:id", oturumGerekli, iadeOnayla);
Altay Kargo API'sinin iki ucu. oturumGerekli yalnız girişi denetler, rolü ve sahipliği sormaz.

Kodu satır satır oku. İlk uç adresteki numarayı alır ve kaydı yalnız bu numarayla arar. Sorguda oturumdaki kullanıcıdan hiç söz edilmez.

oturumGerekli ara katmanı yalnız girişin yapılıp yapılmadığına bakar; kimin giriş yaptığını karara katmaz. Son satırdaki iade ucu da aynı ara katmanla korunur. Oturum açmış her kullanıcı, uç adresini bilirse iadeyi onaylar.

İki hata aynı kalıptan çıkar: kimlik doğrulama var, yetkilendirme yok. Kimlik doğrulama "sen kimsin" sorusunu yanıtlar. Yetkilendirme "bunu yapabilir misin" sorusunu yanıtlar. Birincisinin varlığı ikincisini kendiliğinden getirmez.

GET /api/siparisler/55121 HTTP/1.1
Host: api.altaykargo.example
Authorization: Bearer eyJhbGciOi...   (sub: 4410, rol: musteri)

HTTP/1.1 200 OK
Content-Type: application/json

{ "id": 55121, "musteriId": 5932, "alici": "Selin Y.", "adres": "Moda Cd. 14, Kadıköy", "durum": "kargoda" }
4410 numaralı müşteri kendi token'ıyla 5932 numaralı müşterinin siparişini istiyor ve kaydı alıyor.

Şimdi isteği oku. Token'daki sub değeri 4410, yanıttaki musteriId ise 5932'dir. Sunucu bu farkı görmez ve 200 yanıtıyla kaydı döner.

Yanıtta alıcının adı ve adresi de vardır. Tek istekte sızan veri azdır. Ama sipariş numaraları ardışık olduğu için aynı istek bir döngüyle binlerce kez tekrarlanabilir.

Geniş çaplı kişisel veri sızıntıları çoğu zaman bu desenle büyür: bir takip ucu ya da fatura indirme adresi, numarayı sırayla artıran bir betikle taranır. Testte bunu iki hesapla doğrularsın. A hesabıyla kendi kaydının numarasını alır, B hesabının token'ıyla aynı numarayı istersin. Bunu yalnız kendi lab ortamında ya da yazılı iznin olan bir hedefte dene.

// Güvenli: rol ara katmanda, sahiplik sorgunun içinde
app.get("/api/siparisler/:id", oturumGerekli, async (req, res) => {
  const s = await db.siparis.findFirst({
    where: { id: Number(req.params.id), musteriId: req.oturum.kullaniciId },
  });
  if (!s) return res.status(404).json({ hata: "sipariş bulunamadı" });
  res.json(s);
});
app.post("/api/admin/iade-onayla/:id", oturumGerekli, rolGerekli("yonetici"), iadeOnayla);
Sahiplik sorgunun içine, rol ayrı bir ara katmana taşındı.

Düzeltmede iki katman ayrı çalışır. İlk uçta sahiplik sorgunun içindedir: kayıt hem numarasıyla hem oturumdaki kullanıcı kimliğiyle aranır. Başkasının numarasını gönderen biri boş sonuç alır ve 404 görür. 403 yerine 404 dönmek, o numarada bir kaydın var olup olmadığını da saklar.

İkinci uçta rolGerekli ara katmanı devreye girer ve yönetici olmayan isteği işleme varmadan keser. Rol, imzası doğrulanmış token'dan ya da sunucudaki oturum kaydından okunur; istek gövdesinden okunmaz. İki katmanı birlikte kurman gerekir.

Rol doğru olsa bile kayıt başkasının olabilir. Kayıt kendine ait olsa bile işlem role kapalı olabilir. Sahada en sık görülen hata, yeni bir ucu eski bir uçtan kopyalarken katmanlardan birini unutmaktır.

Derinleş:Rastgele kimlik BOLA'yı kapatır mı

Bazı ekipler sıralı numaraları UUID gibi tahmin edilemeyen kimliklerle değiştirip sorunu kapandı sayar. Bu adım taramayı zorlaştırır, açığı kapatmaz. Kimlikler başka yollardan sızar: paylaşılan bir bağlantıda, bir e-posta bildiriminde, bir listeleme ucunun yanıtında, tarayıcı geçmişinde ya da bir hata kaydında.

Saldırgan tek bir yabancı kimliği ele geçirdiğinde, sahiplik denetimi yoksa kaydı yine okur. OWASP de API1 maddesinde rastgele ve tahmin edilemeyen kimlikleri önerir, ama bunu yetkilendirme denetiminin yanında ek bir katman olarak sayar.

Tahmin edilemeyen kimlik numaralandırmayı yavaşlatır; kaydın kime ait olduğunu sormanın yerini tutmaz. Aynı durum e-posta adresi, telefon numarası ya da kullanıcı adıyla çağrılan uçlar için de geçerlidir. Bu değerler zaten başkalarınca bilinir ve gizli sayılamaz. Kod incelemesinde bir uç rastgele kimlik kullanıyor diye sahiplik filtresini atlama.

Etkinlik 3 / 6

Aşağıdaki isteği, yalnız kendi siparişin olan 55087 numaralı kaydı isteyecek biçimde düzenle.

İsteği tamamla ve gönder.

GET /api/siparisler/{id} HTTP/1.1
Authorization: Bearer <musteri-token>
id

Tespit ve sık hatalar

Açığı üretimde değil tasarımda yakalamak en ucuzudur. OpenAPI belgesi her ucun yolunu, yöntemini ve güvenlik gereksinimini tek dosyada tutar. Belgedeki security alanı ve kapsamlar (scope), hangi ucun hangi yetkiyle çağrılacağını gösterir. Bu belgeden bir uç ve rol tablosu çıkarıp çalışan uygulamanın yönlendirme listesiyle karşılaştırırsın.

Belgede olup kodda farklı korunan ya da kodda olup belgede hiç geçmeyen her uç, denetlenmemiş bir uç adayıdır. Aynı tablo otomatik testlere de kaynak olur.

Test, müşteri token'ıyla her yönetici ucunu çağırır ve 403 bekler. Bir müşterinin token'ıyla başka müşteriye ait kaydı ister ve 404 bekler. Bu iki test her yeni uçta kendiliğinden çalışırsa, unutulan katman sürüme çıkmadan yakalanır.

paths:
  /api/siparisler/{id}:
    get:
      security:
        - oauth2: [siparis:oku]
  /api/admin/iade-onayla/{id}:
    post:
      security:
        - oauth2: [iade:onayla]
Altay Kargo'nun OpenAPI belgesinden iki uç. Kapsamlar her ucun hangi yetkiyle çağrılacağını yazar.

Çalışan sistemde BOLA denemesi kayıtta belirgin bir iz bırakır. Aynı token kısa sürede çok sayıda farklı kayıt numarası ister. Numaralar çoğu zaman sıralıdır.

Yanıtların çoğu 404 ya da 403'tür, araya birkaç 200 karışır. Normal bir müşteri birkaç siparişine bakar; bir dakikada onlarca farklı numara istemez. BFLA denemesinde ise sıradan bir rol, yönetici yoluna istek atar ve 403 alır.

Tek bir 403 kullanıcı hatası olabilir. Aynı oturumdan art arda gelen yönetici yolu istekleri ise keşif işaretidir. Bu iki deseni SIEM'de kural olarak yazarsan, açık kapanmadan önce bile istismar denemesi görünür olur.

Etkinlik 4 / 6

Altay Kargo API kaydında BOLA denemesine işaret eden satırları seç.

Şüpheli satırlara tıkla. Birden fazla olabilir.

Etkinlik 5 / 6

Bir API ucu yalnız mobil uygulamadan çağrılacak diye tasarlandı ve rol denetimi eklenmedi. Bu tasarımın riski nedir?

Servisler arası çağrılar

Altay Kargo'da mobil uygulamanın arkasında tek bir sunucu yoktur. Sipariş servisi faturayı ister, fatura servisi müşteri servisinden adres çeker. Her biri ötekine bir API isteği gönderir. Bu çağrılarda ortada bir kullanıcı olmayabilir, ama kimlik yine gereklidir.

Servisler genellikle OAuth 2.0'ın istemci kimliği akışını (client credentials) kullanır. Servis kendi istemci kimliği ve gizli değeriyle yetkilendirme sunucusundan bir erişim token'ı alır.

Token, hangi servise ait olduğunu ve hangi kapsamlarla çağrı yapabileceğini taşır. Fatura servisi yalnız adres okuma kapsamı alır; müşteri kaydını silme kapsamı almaz. Çağrılan servis token'ı doğrular, kapsamı istenen işlemle karşılaştırır ve uymayan isteği reddeder.

İç ağda olmak tek başına güven vermez. Ağ geçidinin arkasındaki bir servis ele geçirilirse, denetimsiz komşu servis saldırganın bir sonraki durağı olur. Her servis gelen isteği dışarıdan gelmiş gibi karşılar: kimliği doğrular, kapsamı denetler, kayıt sahipliğini sorgunun içinde arar. Kimliği ağ düzeyinde güçlendirmek için karşılıklı TLS (mTLS) eklenebilir.

mTLS hangi servisin bağlandığını kanıtlar, ama o servisin hangi işlemi yapabileceğine karar vermez. O karar yine uygulamadaki rol ve kapsam denetimine kalır. Bütün servislerde paylaşılan geniş kapsamlı tek bir token ise en sık görülen tasarım hatalarındandır. O token sızarsa her kapı aynı anda açılır.

Etkinlik 6 / 6

Doğru mu, yanlış mı?

İç ağdaki iki mikroservis arasındaki API çağrısında da rol ve sahiplik denetimi her istekte çalışmalıdır.

Bu derste bir hata mı gördün? Bildir