İçeriğe geç
Sedat Özdemir
Yazılar

apisecurity

Görünmez Kapılar: API Dünyasında 'Bana Sormadan İçeri Buyur' Mantığı

Modern web uygulamalarında en çok karşımıza çıkan ve çoğu zaman otomatik araçların gözünden kaçan BOLA (Broken Object Level Authorization) zafiyetine derinlemesine bir bakış.

Sedat Özdemir
· 4 dk okuma

Eskiden bir web sitesini hacklemek demek, SQL Injection ile veritabanını patlatmak veya XSS ile admin panelini çalmak demekti. Şimdi ise devir değişti. Artık kimse formlara ' OR 1=1 -- yazmıyor (yazan var da, genelde WAF'a takılıp ağlıyorlar). Bugünün asıl mevzusu: Mantık hataları. Artık kimse 8 karakterli parolalara güvenmiyor, peki ya JSON paketlerinin arasına gizlenmiş, sizin dahi kontrol etmeyi unuttuğunuz o küçük ID parametrelerine ne kadar güveniyorsunuz?

Bir Red Team Lead olarak sahada en çok karşılaştığım, bazen 'testCompany' gibi dev yapılarda bile can yakan o sinsi arkadaştan bahsedeceğim bugün: BOLA (Broken Object Level Authorization), nam-ı diğer IDOR'un modern ve daha yakışıklı kardeşi.

Mevzu Nedir? Bu BOLA da Nereden Çıktı?

Aslında hikaye çok basit. Bir web uygulamanız var, her şey modern; React, Vue ya da Angular ile süslenmiş bir ön yüz, arkada ise fıstık gibi çalışan bir REST API. Kullanıcı sisteme giriş yapıyor, kendi profilini görmek istiyor. Tarayıcı arkadan şöyle bir istek atıyor:

GET /api/v1/user/settings?id=1234

Sunucu bakıyor, 'Hah, bu Sedat. Giriş yapmış mı? Yapmış. Token'ı geçerli mi? Geçerli. Tamam o zaman, 1234 ID'li kullanıcının verilerini gönderelim.' diyor. İşte tam o saniyede bir şeyler eksik kalıyor. Sunucu Sedat'ın giriş yapıp yapmadığını (Authentication) kontrol etti ama Sedat'ın gerçekten 1234 numaralı veriye erişme hakkı (Authorization) olup olmadığını sormadı.

Ben buradaki 1234 değerini 1235 yaptığımda, yan masadaki arkadaşın gizli bilgilerini görüyorsam, tebrikler; nur topu gibi bir BOLA zafiyetiniz var demektir.

Neden Otomatik Araçlar Bunu Yakalayamıyor?

En çok sorulan sorulardan biri bu: 'Sedat, biz dünyaca ünlü vulnerability scanner kullanıyoruz, o neden bulmadı?'

Çünkü scanner'lar 'business logic' dediğimiz iş mantığını anlamazlar. Scanner bir isteğe 200 OK aldığında her şeyin yolunda olduğunu sanır. Oysa o 200 OK'in içinde senin görmemen gereken bir başkasının kredi kartı numarası ya da ev adresi olabilir. Bunu ancak bir insan gözü (ya da çok iyi kurgulanmış bir Red Team senaryosu) fark edebilir.

Sahadan Bir Senaryo: testCompany Dashboard Kazası

Geçenlerde 'testCompany' için yaptığımız bir sızma testinde, uygulamanın fatura indirme kısmına denk geldik. Sistem çok güvenli görünüyor; JWT'ler havada uçuşuyor, request'ler imzalanıyor falan. Ama bir endpoint dikkatimi çekti:

POST /api/export/invoice Body: { "invoice_uuid": "inv_8822-1100", "user_context": "77" }

Burada user_context parametresini 78 yaptığımda, sistem bana 'Hata! Yetkisiz Erişim' demiyor, çat diye 78 numaralı kullanıcının faturasını PDF olarak mailime gönderiyordu. İşte biz buna 'Hatalı Yetki Kontrolü' diyoruz. Kod yazarken 'Zaten token'ı var, token'daki user_id ile body'deki user_context aynıdır herhalde' diye varsaymak, güvenliği şansa bırakmaktır.

Kodun Canına Okumadan Önce: Hatalı Yaklaşım

Gelin, hep beraber bir 'günah keçisi' kod yazalım. Aşağıdaki örnekte, Node.js ve Express kullanan bir API endpoint'i hayal edelim.

// ZARARLI/ZAFİYETLİ ÖRNEK (Lütfen bunu yapmayın)
app.get('/api/v1/get-profile-details', async (req, res) => {
    const targetUserId = req.query.id; // Kullanıcıdan gelen ID

    // Sadece giriş yapmış mı diye bakıyoruz (Zayıf kontrol)
    if (!req.isAuthenticated()) {
        return res.status(401).send('Önce bir giriş yap bakalım.');
    }

    // Veritabanından veriyi çekip sorgusuz sualsiz döndürüyoruz
    const userData = await db.users.findOne({ id: targetUserId });
    res.json(userData);
});

Buradaki sıkıntı bariz değil mi? req.query.id parametresi üzerinde hiçbir sahiplik kontrolü yok. Ben tarayıcıdan ?id=999 yazarsam ve eğer o ID sistemde varsa, veriyi alırım.

Peki, Nasıl Savunacağız? (The Hardening Way)

Savunma tarafında işimiz aslında net ama titizlik istiyor. Her veri erişiminde şu soruyu sormak zorundayız: 'Bu verinin sahibi, bu isteği yapan kişi mi?'

İşte daha sağlam bir yaklaşım:

// GÜVENLİ/HARDENED ÖRNEK
app.get('/api/v1/get-profile-details', async (req, res) => {
    // Kullanıcının kimliğini JWT veya Session üzerinden alıyoruz (Sunucu taraflı güven)
    const authenticatedUserId = req.user.id;
    const targetUserId = req.query.id;

    // 1. Kural: Kullanıcı sadece kendi ID'sini mi istiyor?
    // 2. Kural: Eğer başkasının verisini istiyorsa (Örn: Admin ise), buna yetkisi var mı?
    if (authenticatedUserId !== targetUserId && !req.user.isAdmin) {
        console.error(`Yetkisiz erişim denemesi! User ${authenticatedUserId} -> Target ${targetUserId}`);
        return res.status(403).json({ error: 'Bu veriyi görmeye yetkin yok dostum.' });
    }

    const userData = await db.users.findOne({ id: targetUserId });
    
    if (!userData) {
        return res.status(404).send('Bulunamadı.');
    }

    res.json(userData);
});

Red Team Gözüyle Ekstra İpuçları

Sadece ID'yi kontrol etmek yetmez. Bazı durumlarda saldırganlar 'ID Enumeration' dediğimiz yöntemi kullanır. Eğer ID'leriniz 1, 2, 3... diye ardışık gidiyorsa, bir script yazıp tüm veritabanını duman etmek sadece birkaç saniye sürer.

  • UUID Kullanın: Tahmin edilmesi zor (v4) ID'ler kullanmak, saldırganın işini zorlaştırır ama tek başına bir güvenlik önlemi değildir (BOLA hala orada olabilir, sadece bulunması zorlaşır).
  • Global Authorization Middleware: Her endpoint'te tek tek kontrol yazmak yerine, global bir yetkilendirme katmanı (middleware) kurgulayın. Hata payını azaltır.
  • Loglama ve Monitoring: Bir kullanıcı kısa sürede yüzlerce farklı ID için istek atıyorsa, orada bir şeyler ters gidiyordur. WAF kurallarınızı veya SIEM alarm mekanizmalarınızı buna göre besleyin.

Modern web dünyasında 'İçerisi güvenli, kapıyı kilitledim' demek yetmiyor. Her odanın kapısını ayrı ayrı kilitlemek ve anahtarı sadece odanın sahibine vermek zorundasınız. Unutmayın, en zayıf halka her zaman en çok güvendiğiniz ama kontrol etmeyi unuttuğunuz o küçük parametredir.

Bir sonraki teknik fırtınada görüşmek üzere, kodunuza ve yetkilerinize sahip çıkın!

İlgili yazılar