Eskiden siber güvenlik dünyası biraz daha 'vahşi batı' gibiydi. Bir giriş formuna tek tırnak atıp veritabanının hata verip vermediğine bakar, SQL Injection peşinde koşardık. Bugün ise WAF'lar (Web Application Firewall), modern framework'ler ve gelişmiş tarayıcı korumaları sayesinde o eski, kaba saldırılar yavaş yavaş tarih oluyor. Artık kimse 8 karakterli basit parolalara güvenmiyor, peki ya JSON paketlerimizin içine sakladığımız o masum görünen field'lara ne kadar güveniyoruz?
Şunu bir düşün: Uygulamanın ön yüzü (frontend) tertemiz, tüm validasyonlar yapılmış, her şey pırıl pırıl görünüyor. Ama arka planda, o API uç noktası kendisine gönderilen her şeyi 'yutmaya' hazır bekliyorsa? İşte bugün, modern web dünyasının en sinsi açıklarından biri olan Mass Assignment (Kitlesel Atama) ve onun kuzeni IDOR (Insecure Direct Object Reference) üzerine biraz kafa yoralım. Kahveni tazelediysen, testCompany simülasyon labımıza geçelim.
Modern Web'in Yumuşak Karnı: API'lar
Günümüzde monolitik yapılardan mikroservislere geçtik. Bu da demek oluyor ki, her şey API'lar üzerinden dönüyor. Mobil uygulamalar, React/Vue tabanlı frontend'ler hep aynı JSON dilini konuşuyor. Bir Red Teamer olarak benim en sevdiğim an, bir geliştiricinin 'Nasıl olsa bu alanı frontend'den göndermiyorum, o yüzden güvendeyim' dediği andır.
Sistem şöyle işler: Sen bir profil güncelleme sayfası yaparsın. Kullanıcı adını, soyadını ve biyografisini günceller. Frontend'den giden istek şuna benzer:
// POST /api/v1/user/update
{
"name": "Sedat",
"surname": "Ozdemir",
"bio": "Siber güvenlik meraklısı bir geek."
}
Buraya kadar her şey normal. Ancak arka tarafta, testCompany bünyesindeki o aceleci geliştirici (hepimiz bazen öyleyiz), gelen bu JSON nesnesini alıp doğrudan veritabanı modeline 'bind' ediyorsa (haritalıyorsa) tehlike çanları çalmaya başlar.
Mass Assignment: Kapıyı Aralamak
Eğer ben bir saldırgan olarak bu isteği araya girip (Burp Suite gibi bir araçla) yakalarsam ve içine backend modelinde olduğunu tahmin ettiğim bir alanı eklersem ne olur?
// Manipüle edilmiş POST /api/v1/user/update
{
"name": "Sedat",
"surname": "Ozdemir",
"bio": "Siber güvenlik meraklısı bir geek.",
"is_admin": true,
"role": "super_user",
"balance": 999999
}
Eğer backend tarafında kullanılan ORM (Object-Relational Mapping) aracı, gelen bu ekstradan is_admin veya role alanlarını sorgusuz sualsiz veritabanına yazıyorsa, tebrikler; tek bir HTTP isteğiyle sistemde en yetkili kullanıcı oldun. Bu duruma Mass Assignment diyoruz.
Bu hatayı engellemenin yolu 'Blacklist' (yasaklı liste) kullanmak değildir. 'Hangi alanların güncellenemeyeceğini' listelemek her zaman bir açığa neden olur. Çözüm her zaman 'Whitelist' (izinli liste) kullanmaktır. Yani bir DTO (Data Transfer Object) kullanmalısın.
Örnek Güvenli Yaklaşım (Pseudo-code):
// Yanlış: Gelen her şeyi modele basmak
async function updateProfile(req, res) {
const user = await User.findById(req.user.id);
Object.assign(user, req.body); // TEHLİKE! req.body içinde ne varsa user'a yazar.
await user.save();
}
// Doğru: Sadece belirli alanları kabul etmek
async function updateProfile(req, res) {
const user = await User.findById(req.user.id);
// Sadece izin verilen alanları elle eşleştiriyoruz
user.name = req.body.name;
user.surname = req.body.surname;
user.bio = req.body.bio;
await user.save();
}
IDOR: 'Benim Olmayan Veriye Dokunabilir miyim?'
Mass Assignment ile yetki yükselttik, peki ya başkasının verisine sızmak? İşte burada sahneye IDOR çıkıyor. IDOR, bir nesneye erişirken kullanılan referansın (genelde bir ID) yetkilendirme kontrolü yapılmadan kullanılmasıdır.
Bir e-ticaret sitesinde olduğunu düşün. Faturanı görüntülüyorsun: https://example.com/api/v1/invoice/1005.
Buradaki 1005 senin fatura numaran. Eğer ben bu numarayı 1004 yaparsam ve testCompany sistemleri bana o faturayı 'Sen kimsin? Bu fatura senin mi?' diye sormadan gösterirse, geçmiş olsun.
Bunu sadece basit bir sayısal ID olarak düşünme. UUID (Universally Unique Identifier) kullanmak bir güvenlik önlemi değildir, sadece 'tahmin edilebilirliği' zorlaştırır (Security by obscurity). Eğer ben bir şekilde o uzun karmaşık ID'yi bulursam ve sistem hala yetki kontrolü yapmıyorsa, IDOR hala oradadır.
Peki Ne Yapmalı? (Defensive Mindset)
Bak dostum, bu işin sırrı 'Asla güvenme' prensibinde (Zero Trust) yatıyor.
- Input is Evil: Kullanıcıdan gelen her veri, en yetkili kullanıcıdan gelse bile, potansiyel olarak zehirlidir. Her zaman validasyondan ve sanitasyondan geçmelidir.
- Yetkilendirme Katmanı: Sadece 'Kullanıcı sisteme giriş yapmış mı?' (Authentication) kontrolü yetmez. 'Bu kullanıcı, talep ettiği bu spesifik kaynağa (ID: 1005) erişme yetkisine sahip mi?' (Authorization) kontrolü her fonksiyonun başında yapılmalıdır.
- UUID ve Hash Kullanımı: Veritabanı ID'lerini (1, 2, 3...) dış dünyaya sızdırma. Onun yerine rastgele üretilmiş string'ler kullan. Bu, saldırganın 'Enumeration' (numaralandırma) yaparak tüm veritabanını çekmesini zorlaştırır.
- Audit Logs: Kim, ne zaman, hangi ID'li kaynağa erişmeye çalıştı? Başarısız yetki denemelerini logla ki, birisi sistemini 'scout' ederken (keşif yaparken) onu fark edebilesin.
Sonuç Niyetine
Siber güvenlik bir varış noktası değil, bir yolculuk. Bugün API güvenliğini konuşuyoruz, yarın belki de yapay zeka ajanlarının birbirine sızmasını konuşacağız. Ama temel hiç değişmeyecek: Mantık hataları, en karmaşık şifreleme algoritmalarından bile daha yıkıcıdır.
Kod yazarken veya bir sistemi test ederken kendine hep şu soruyu sor: 'Ben bu veriyi buraya gönderiyorum ama arka planda bu verinin başına neler gelebilir?'
Bir sonraki teknik derin dalışımızda görüşmek üzere, terminalin başından çok uzaklaşma!
