Eskiden bir web sitesini hacklemek denildiğinde akla hemen o meşhur <script>alert('XSS')</script> kodları veya veritabanını ayağa kaldıran OR 1=1 SQL enjeksiyonları gelirdi. Güvenlik duvarları (WAF) bu paternleri tanımayı öğrendi, yazılımcılar sanitize fonksiyonlarını ezberledi. Ancak bugün, oyunun kuralları tamamen değişti. Artık kimse ön kapıdan balyozla girmeye çalışmıyor; herkes arka taraftaki, dokümantasyonu bile yapılmamış o küçük API endpoint'inin 'mantık' hatasını arıyor.
API'ler artık internetin sinir sistemi haline geldi. Mobil uygulamalardan IoT cihazlarına, mikroservis mimarilerinden üçüncü parti entegrasyonlara kadar her şey JSON formatında akan sessiz bir veri trafiğiyle dönüyor. İşte bu sessizlik, beraberinde büyük bir kör nokta getiriyor. Red Team operasyonlarımızda sıkça gördüğümüz bir gerçek var: Bir sistemi ne kadar çok WAF veya IPS ile çevrelerseniz çevreleyin, iş mantığındaki (Business Logic) bir hata, tüm o savunma katmanlarını tek bir 'Request' ile bypass edebiliyor.
BOLA: En Sevdiğimiz Ama En Tehlikeli Misafir
OWASP API Security Top 10 listesinin zirvesinden inmeyen bir arkadaşımız var: BOLA (Broken Object Level Authorization). Eski adıyla IDOR. Ama API dünyasında çok daha sinsi.
Senaryoyu düşünelim: Bir e-ticaret platformu olan testCompany için bir mobil uygulama geliştirildiğini varsayalım. Kullanıcı kendi siparişlerini görüntülemek istediğinde uygulama şu endpoint'e istek atıyor:
GET /api/v1/orders/ORD-8821
Burada güvenlik ekibinin kontrol etmesi gereken şey sadece 'Bu kullanıcı giriş yapmış mı?' (Authentication) değil, 'Bu kullanıcı 8821 numaralı siparişi görmeye yetkili mi?' (Authorization) sorusudur. Çoğu zaman geliştiriciler hız uğruna sadece ilkini kontrol ederler. Bir saldırgan olarak ben, proxy (örneğin Burp Suite) üzerinden bu isteği yakalayıp ORD-8821 değerini ORD-8822 yaptığımda, eğer başka bir müşterinin sipariş detaylarını (adresi, telefonu, kart son 4 hanesi) görüyorsam, geçmiş olsun; BOLA kucağımızda demektir.
Zararsızlaştırılmış Örnek (Defanged Payload):
GET /api/v1/user/1005/profile HTTP/1.1
Host: api.example.com
Authorization: Bearer [JWT_TOKEN_OF_USER_1005]
# Saldırganın değiştirdiği istek:
GET /api/v1/user/1001/profile HTTP/1.1
Host: api.example.com
Authorization: Bearer [JWT_TOKEN_OF_USER_1005]
Eğer sunucu 200 OK ile 1001 numaralı kullanıcının verisini dönerse, sistemin yetkilendirme katmanı çökmüş demektir.
Mass Assignment: 'Sen Sormadan Ben Söyleyeyim'
Bir diğer favori konumuz ise Mass Assignment. Modern framework'ler (Node.js, Ruby on Rails, Spring vb.), istemciden gelen JSON objesini doğrudan veritabanı modeline map etmeyi çok severler. Bu, yazılımcı için büyük kolaylıktır ama güvenlikçinin kabusudur.
testCompany kullanıcı kayıt formunda sadece username, email ve password alanları olduğunu düşünelim. Ancak veritabanındaki User tablosunda bir de is_admin veya account_balance kolonu var.
Saldırgan kaydolurken şöyle bir payload gönderir:
{
"username": "sedat_hacker",
"email": "[email protected]",
"password": "P4ssw0rd!",
"is_admin": true,
"account_balance": 999999
}
Eğer API katmanında bir 'Allow-list' (izin verilen alanlar listesi) tanımlanmadıysa, uygulama bu JSON'u alır ve safça veritabanına yazar. Artık sisteme admin olarak giriş yaptım. Tebrikler, artık içerideyim!
Broken Function Level Authorization: Admin Paneli Her Yerde
Bazen de yetkilendirme hataları 'objeler' üzerinde değil, 'fonksiyonlar' üzerinde gerçekleşir. Bir API'nin /api/v1/users/me endpoint'ini herkes çağırabilir. Peki ya /api/v1/users/delete/1001?
Birçok durumda, sadece arayüzde (frontend) 'Sil' butonunun gizlenmesi bir güvenlik önlemi sanılıyor. Oysa terminalin başında oturan biri için arayüz sadece bir tavsiyedir. Eğer API tarafında bu fonksiyonun sadece role: admin olan biri tarafından çağrılabileceği kontrol edilmiyorsa, standart bir kullanıcı tüm veritabanını silebilir.
Peki Ne Yapmalı? (Defensive Mindset)
Sahada gördüğüm en büyük yanlış, güvenliği sadece 'dışarıdan eklenen bir katman' olarak görmek. Güvenlik, kodun içine işlenmelidir. İşte testCompany ve benzeri yapılar için birkaç altın kural:
- Güvenli ID Kullanımı: Sıralı (incremental) ID'ler (
1, 2, 3...) yerine her zaman UUID veya GUID kullanın. Bu, saldırganın 'bir sonraki kayıt nedir?' diye tahmin yürütmesini (enumeration) imkansız hale getirir. - Explicit Mapping: İstemciden gelen her şeyi doğrudan veritabanına basmayın. Sadece ihtiyacınız olan alanları seçen DTO (Data Transfer Object) yapıları kullanın.
- Her İstekte Yetki Kontrolü: 'Kullanıcı login oldu, o zaman her şeyi yapabilir' mantığını unutun. Her veri erişiminde (Object Level) 'Bu veri bu kişiye mi ait?' kontrolünü yapın.
- Logging ve Monitoring: API'lerinizde normalin dışında bir 403 (Forbidden) veya 404 (Not Found) artışı varsa, biri muhtemelen endpoint'lerinizi enumerate ediyordur. Alarm zilleri çalmalı.
Terminalin diğer tarafında işler her zaman beklediğiniz gibi gitmez. Unutmayın, en güvenli API, dışarıya hiç veri sızdırmayan değil; hangi veriyi, kime, neden verdiğini her saniye bilen API'dir. Bir sonraki sızma testinde veya kod analizinde, sadece 'Hangi endpoint çalışıyor?' diye değil, 'Bu endpoint'i nasıl kötüye kullanabilirim?' diye sormayı alışkanlık haline getirin.
Kodunuz temiz, payload'larınız (eğitim amaçlı!) keskin olsun. Görüşmek üzere.
