API'leriniz Sızdırıyor Olabilir: Her Geliştiricinin Bilmesi Gereken 5 Kritik Güvenlik Zafiyeti

BOLA, Kırık Kimlik Doğrulama, IDOR... Kulağa karmaşık gelen ama her gün yüzlerce API'ı etkileyen bu kritik zafiyetleri, basit açıklamalar ve gerçek kod örnekleriyle öğrenin. API'larınızı nasıl bir kaleye dönüştüreceğiniz bu rehberde.
Giriş: O Son Commit'ten Önceki Sessizlik
Tebrikler. Yeni özelliğiniz tamamlandı, testler başarılı ve API'ınız adeta bir saat gibi çalışıyor. Performans harika, fonksiyonellik tam istediğiniz gibi. Tam "deploy" butonuna basmak üzeresiniz. Peki, bir an durup düşündünüz mü? Bu API ne kadar güvenli? Çoğu geliştirici için güvenlik, bir özellikten çok, "sonra halledilecek" bir angarya gibi gelir. İşte bu düşünce, milyonlarca dolarlık veri sızıntılarının, itibar kayıplarının ve kullanıcı güveninin yerle bir olmasının başlangıç noktasıdır.
Bu rehberde, OWASP'ın korkutucu listelerindeki soyut maddeleri bir kenara bırakıp, her geliştiricinin anlayabileceği, gerçek hayattan senaryolar ve kod örnekleriyle en kritik 5 API güvenlik zafiyetini masaya yatırıyoruz. Amacımız, sizi bir siber güvenlik uzmanı yapmak değil, yazdığınız her kod satırına bir "güvenlik gözlüğüyle" bakmanızı sağlamaktır.
1. Kırık Nesne Seviyesi Yetkilendirme (BOLA - Broken Object Level Authorization)
Nedir? En yaygın ve en tehlikeli API zafiyetidir. En basit haliyle, bir kullanıcının, normalde erişim hakkı olmaması gereken başka bir kullanıcının verilerine erişebilmesidir. API, "kimsin?" diye sorar (Authentication - Kimlik Doğrulama) ama "bu veriye bakmaya yetkin var mı?" diye sormayı unutur (Authorization - Yetkilendirme).
Saldırı Senaryosu
Bir e-ticaret sitesinde siparişlerinizi görüntülediğiniz endpoint'i düşünün: GET /api/orders/123. Bu istek, 123 ID'li siparişin detaylarını getirir. Saldırgan, bu isteği yakalar ve ID'yi basitçe değiştirir: GET /api/orders/124. Eğer API'da BOLA zafiyeti varsa, sunucu bu isteği sorgusuz sualsiz yerine getirir ve saldırgana başkasının sipariş detaylarını (adresi, aldığı ürünleri, ödediği tutarı) altın tepside sunar.
Zafiyetli Kod Örneği (Node.js/Express)
// YANLIŞ: Sadece giriş yapmış olmak yeterli görülüyor.
router.get('/orders/:orderId', isAuthenticated, async (req, res) => {
try {
const order = await Order.findById(req.params.orderId);
if (!order) {
return res.status(404).send('Sipariş bulunamadı.');
}
res.json(order); // KONTROL YOK! Sipariş bu kullanıcıya mı ait?
} catch (err) {
res.status(500).send('Sunucu hatası.');
}
});
Güvenli Kod Örneği
// DOĞRU: Kullanıcının kimliği ile verinin sahipliği karşılaştırılıyor.
router.get('/orders/:orderId', isAuthenticated, async (req, res) => {
try {
const order = await Order.findById(req.params.orderId);
// req.user objesi, isAuthenticated middleware'i tarafından JWT'den çözülerek eklenir.
if (!order || order.userId.toString() !== req.user.id) {
// Hem sipariş yoksa hem de sipariş başkasına aitse 404 dönmek,
// saldırgana bilgi sızdırmamak için daha güvenlidir.
return res.status(404).send('Sipariş bulunamadı.');
}
res.json(order);
} catch (err) {
res.status(500).send('Sunucu hatası.');
}
});
Nasıl Önlenir?
- Her endpoint'te, veriye erişmeye çalışan kullanıcının o verinin sahibi olup olmadığını kontrol edin.
- Bu kontrolleri merkezi bir yetkilendirme katmanında (middleware vb.) yapmayı düşünün.
- Tahmin edilebilir ID'ler (1, 2, 3...) yerine tahmin edilmesi zor olan UUID'ler kullanın. Bu, zafiyeti ortadan kaldırmaz ama saldırganın işini zorlaştırır.
2. Kırık Kimlik Doğrulama (Broken Authentication)
Nedir? Kullanıcıların kimliğini doğrulama süreçlerindeki hatalardır. Zayıf parola politikaları, sızdırılan session ID'leri, ve en önemlisi, JWT'nin (JSON Web Token) yanlış uygulanması bu kategoriye girer.
Saldırı Senaryosu: JWT Algoritma Manipülasyonu
Bir saldırgan, normalde HS256 gibi bir algoritma ile imzalanmış bir JWT alır. Token'ın payload kısmını (kullanıcı ID'si, rolü vb.) istediği gibi değiştirir. Sonra, başlık (header) kısmındaki alg (algoritma) parametresini none olarak değiştirir ve imzayı siler. Eğer sunucudaki JWT kütüphanesi bu duruma karşı yapılandırılmamışsa, "algoritma yok, o halde imza kontrolüne de gerek yok" diyerek bu sahte token'ı geçerli sayabilir. Sonuç: Saldırgan, istediği herhangi bir kullanıcı, hatta bir admin olabilir.
Güvenli Kod Örneği (JWT Doğrulama)
// DOĞRU: Hangi algoritmaların kabul edileceği açıkça belirtiliyor.
const jwt = require('jsonwebtoken');
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET, {
algorithms: ['HS256'] // Sadece bu algoritmayı kabul et!
});
req.user = decoded;
} catch (err) {
// Token geçersiz (imza hatası, süresi dolmuş, vb.)
return res.status(401).send('Geçersiz Token.');
}
Nasıl Önlenir?
- Brute-force saldırılarına karşı giriş denemelerini sınırlayın (rate limiting).
- Zayıf parolaları engelleyin ve parola sıfırlama mekanizmalarınızın güvenli olduğundan emin olun.
- JWT secret anahtarınızı uzun, karmaşık ve tahmin edilemez yapın. Asla kodun içinde hard-coded bırakmayın, ortam değişkeni olarak saklayın.
- JWT kütüphanenizde
alg: nonegibi zafiyetlerin engellendiğinden emin olun ve kabul edilen algoritmaları daima belirtin.
3. Kırık Nesne Özelliği Seviyesinde Yetkilendirme
Nedir? Bu zafiyet iki şekilde ortaya çıkar. Aşırı Veri Paylaşımı: API'nin, bir nesnenin tüm özelliklerini, hassas olanlar dahil, istemciye göndermesi ve filtreleme işini istemciye bırakmasıdır. Toplu Atama (Mass Assignment): İstemciden gelen veriyi filtrelemeden doğrudan veritabanı nesnesine yazmaktır.
Saldırı Senaryosu
Aşırı Veri Paylaşımı: GET /api/users/me endpoint'i, kullanıcı nesnesini veritabanından olduğu gibi alıp JSON olarak döndürüyor. Frontend sadece name ve email alanlarını gösterse de, tarayıcının ağ (network) sekmesini açan bir saldırgan, yanıtta "userRole": "customer" veya "passwordHash": "..." gibi hassas bilgileri görebilir.
Toplu Atama: Kullanıcı profilini güncellemek için kullanılan endpoint PATCH /api/users/me, gelen JSON gövdesini doğrudan veritabanı modeline uyguluyor. Saldırgan, normalde formda olmayan bir alanı isteğe ekler: { "email": "[email protected]", "isAdmin": true }. Eğer API bu isteği filtrelemezse, saldırgan kendi kendini admin yapar.
Güvenli Kod Örneği (Toplu Atama Önlemi)
// YANLIŞ: Gelen tüm body'yi güncellemeye çalışmak
router.patch('/users/me', isAuthenticated, async (req, res) => {
const user = await User.findByIdAndUpdate(req.user.id, req.body, { new: true });
res.json(user); // Tehlikeli! Kullanıcı kendini admin yapabilir.
});
// DOĞRU: Sadece izin verilen alanları güncellemek
router.patch('/users/me', isAuthenticated, async (req, res) => {
const updates = {};
if (req.body.firstName) updates.firstName = req.body.firstName;
if (req.body.lastName) updates.lastName = req.body.lastName;
// 'isAdmin' veya 'role' gibi alanlar asla istemciden alınmaz.
const user = await User.findByIdAndUpdate(req.user.id, { $set: updates }, { new: true });
// DTO (Data Transfer Object) kullanarak sadece güvenli veriyi geri dön
res.json({ id: user.id, firstName: user.firstName, lastName: user.lastName });
});
Nasıl Önlenir?
- API yanıtlarınızda asla tüm veritabanı nesnesini döndürmeyin. Sadece istemcinin ihtiyaç duyduğu alanları içeren DTO'lar (Data Transfer Objects) veya ViewModel'lar oluşturun.
- Gelen veriyi körü körüne veritabanına yazmayın. Sadece kullanıcının değiştirmesine izin verilen alanları (bir "whitelist" ile) güncelleyin.
4. Kısıtlanmamış Kaynak Tüketimi
Nedir? API'nin, bir istemcinin ne kadar veya ne sıklıkta kaynak (CPU, hafıza, bant genişliği) tüketebileceğine dair bir sınır koymamasıdır. Bu durum, hizmet reddi (Denial-of-Service - DoS) saldırılarına kapı aralar.
Saldırı Senaryosu
Saldırgan, sayfalama (pagination) olmayan bir GET /api/products endpoint'ine saniyede binlerce istek gönderir. Her istek, veritabanında binlerce kaydı sorgular, bu da sunucunun CPU ve hafızasını tüketerek diğer meşru kullanıcılar için hizmet veremez hale gelmesine neden olur. Veya, tek bir istek bile, eğer veritabanında milyonlarca ürün varsa, sunucuyu çökertmeye yetebilir.
Güvenli Kod Örneği (Rate Limiting ve Sayfalama)
const rateLimit = require('express-rate-limit');
// Rate Limiter Middleware'i
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 dakika
max: 100, // Her IP bu pencerede 100 istek yapabilir
standardHeaders: true,
legacyHeaders: false,
});
app.use('/api', apiLimiter); // Tüm /api endpoint'lerine uygula
// Sayfalama (Pagination)
router.get('/products', async (req, res) => {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10;
const skip = (page - 1) * limit;
const products = await Product.find().skip(skip).limit(limit);
res.json(products);
});
Nasıl Önlenir?
- Tüm API endpoint'lerinize makul bir hız limiti (rate limiting) uygulayın.
- Liste döndüren tüm endpoint'lerde mutlaka sunucu taraflı sayfalama (pagination) kullanın ve maksimum limit değeri belirleyin.
- İstek boyutlarına (request payload size) bir üst sınır getirin.
- Zaman aşımı (timeout) süreleri ayarlayarak çok uzun süren istekleri sonlandırın.
5. Kırık Fonksiyon Seviyesi Yetkilendirme
Nedir? BOLA'ya benzer, ancak burada nesnelere değil, fonksiyonlara (yani endpoint'lere) yetkisiz erişim söz konusudur. Kullanıcı rolleri (örn: kullanıcı, editör, admin) arasındaki ayrımın API seviyesinde düzgün bir şekilde uygulanmamasıdır.
Saldırı Senaryosu
API'nızda hem normal kullanıcılar hem de adminler var. Normal bir kullanıcı, kendi profilini GET /api/users/me endpoint'i ile görebiliyor. Saldırgan, API'nızın yapısını tahmin etmeye çalışır ve GET /api/admin/dashboard veya DELETE /api/users/456 gibi endpoint'leri dener. Eğer bu endpoint'ler sadece kimlik doğrulamasına bakıyor ama rol kontrolü yapmıyorsa, normal bir kullanıcı admin yetkileri gerektiren işlemleri gerçekleştirebilir.
Güvenli Kod Örneği (Rol Kontrolü Middleware'i)
// Rolleri kontrol eden bir middleware
const adminOnly = (req, res, next) => {
// req.user, bir önceki kimlik doğrulama middleware'i tarafından eklenmiş olmalı
if (req.user && req.user.role === 'admin') {
next(); // Kullanıcı admin, devam et
} else {
res.status(403).send('Erişim reddedildi. Bu işlem için yetkiniz yok.');
}
};
// Hassas endpoint bu middleware ile korunuyor
router.delete('/users/:userId', isAuthenticated, adminOnly, async (req, res) => {
// ... kullanıcı silme işlemleri ...
});
Nasıl Önlenir?
- "Varsayılan olarak reddet" (deny-by-default) prensibini benimseyin. Bir endpoint için açıkça izin verilmemişse, erişim engellenmelidir.
- Yönetim (admin) fonksiyonlarını normal kullanıcı API'larından ayırın, hatta mümkünse farklı bir API grubunda toplayın.
- Kullanıcı rollerini ve bu rollerin hangi fonksiyonlara erişebileceğini net bir şekilde tanımlayın ve bu kontrolleri merkezi bir yerden yönetin.
Sonuç: Güvenlik Bir Varış Noktası Değil, Bir Yolculuktur
Bu beş zafiyet, buzdağının sadece görünen kısmı olsa da, en sık karşılaşılan ve en yıkıcı olanlarıdır. Unutmayın, API güvenliği tek seferlik bir kontrol listesi değildir. Bu, geliştirme yaşam döngünüzün her adımına entegre etmeniz gereken bir düşünce yapısıdır. Kod yazmaya başladığınız andan itibaren "Bu nasıl istismar edilebilir?" diye sormak, sizi ortalama bir geliştiriciden, sorumlu ve güvenilir bir mühendise dönüştürecek en önemli adımdır.
