IDOR nedir ve neden oluşur?
IDOR (Insecure Direct Object Reference), istemciden gelen bir kimlik değerinin yetki kontrolü yapılmadan doğrudan kullanılmasıdır. Saldırgan, kendi kaydına ait `/api/orders/1001` adresindeki kimliği `/api/orders/1002` olarak değiştirerek başka bir kullanıcının siparişini okuyabiliyor, güncelleyebiliyor veya silebiliyorsa API'de nesne düzeyinde yetkilendirme açığı vardır.
Bu sorun yalnızca GET isteklerinde görülmez. CRUD işlemlerinin tamamı kontrol edilmelidir:
Yetki kontrolünü sorgunun kapsamına dahil edin
En güvenli yaklaşım, kaydı önce genel bir sorguyla bulup sonra uygulama katmanında kontrol etmek yerine, kullanıcı veya kiracı kapsamını doğrudan sorguya eklemektir. Böylece yanlışlıkla başka bir nesnenin döndürülmesi zorlaşır.
Örneğin PHP ve PDO kullanan basit bir güncelleme işleminde, yalnızca kaydın sahibi olan kullanıcı değişiklik yapabilmelidir:
Buradaki `authenticatedUserId` değeri istek gövdesinden, URL'den veya gizli bir form alanından alınmamalıdır. Kimlik, doğrulanmış oturum ya da imzalı erişim belirtecinden sunucu tarafında elde edilmelidir. SQL sorguları parametreli hazırlanmalı; kullanıcı girdisi birleştirilerek sorgu oluşturulmamalıdır.
Çok kiracılı sistemlerde kapsam genellikle `tenant_id` ile genişletilir:
`tenant_id` istemcinin gönderdiği değerden alınmamalı, kullanıcının oturumundaki aktif kiracı ve sunucu tarafındaki üyelik kontrolüyle belirlenmelidir.
Rol, sahiplik ve alan bazlı yetkilendirme
“Giriş yapmış olmak” bir kayda erişim için yeterli değildir. Her işlem için şu sorular açıkça yanıtlanmalıdır:
Yönetici rolleri için de sınırsız erişim varsayımı yerine ayrı bir politika tanımlanmalıdır. Destek personeli yalnızca belirli müşterileri görebilir, finans rolü ödeme alanlarını okuyabilir fakat kullanıcı profilini değiştiremeyebilir.
API tasarımında ve istemci tarafında dikkat edilecek noktalar
Sunucu, URL'deki veya gövde içindeki kullanıcı kimliğine güvenmemelidir. Örneğin `/users/42/orders` isteğinde `42` değeri, oturumdaki kullanıcıyla veya yetkili yönetici kapsamıyla karşılaştırılmalıdır. Daha güvenli bir tasarım, normal kullanıcı işlemlerinde kullanıcı kimliğini sunucunun belirlemesi ve yalnızca yetkili yönetici uç noktalarında açık kimlik parametresi kullanılmasıdır.
JavaScript, mobil uygulama veya HTML arayüzünde butonu gizlemek yalnızca kullanıcı deneyimidir; güvenlik kontrolü değildir. İstek doğrudan HTTP istemcisiyle gönderildiğinde de aynı politika uygulanmalıdır. Ayrıca istemcide önbelleğe alınan yanıtlar, başka kullanıcı oturumlarına sızmayacak şekilde yapılandırılmalıdır.
Yetkisiz bir kaynağın varlığını açığa çıkarmamak için bazı API'ler hem “kayıt yok” hem de “erişim yok” durumunda 404 döndürür. Bu tercih tehdit modeline göre yapılmalıdır. 403 kullanılacaksa yanıt gövdesi başka kullanıcıların kayıtlarının varlığı hakkında gereksiz bilgi vermemelidir.
Test edilebilir bir güvenlik kontrol listesi
Her CRUD uç noktası için en az şu testleri yazın:
Son olarak erişim kararlarını merkezi politika veya servislerde toplamak, aynı kontrolün farklı controller'larda unutulmasını önler. Loglarda kullanıcı, işlem, kaynak türü ve sonuç tutulabilir; ancak erişim belirteci, parola, kişisel veri ve hassas istek gövdeleri kaydedilmemelidir. Yetkilendirme kuralı kodla birlikte gözden geçirilmeli ve her yeni CRUD uç noktası için hem olumlu hem de negatif test eklenmelidir.
IDOR (Insecure Direct Object Reference), istemciden gelen bir kimlik değerinin yetki kontrolü yapılmadan doğrudan kullanılmasıdır. Saldırgan, kendi kaydına ait `/api/orders/1001` adresindeki kimliği `/api/orders/1002` olarak değiştirerek başka bir kullanıcının siparişini okuyabiliyor, güncelleyebiliyor veya silebiliyorsa API'de nesne düzeyinde yetkilendirme açığı vardır.
Bu sorun yalnızca GET isteklerinde görülmez. CRUD işlemlerinin tamamı kontrol edilmelidir:
- GET: Başka kullanıcıların kayıtlarını görüntüleme
- POST: Başka bir kullanıcıya veya kiracıya ait kaynakla ilişki kurma
- PUT/PATCH: Yetkisiz alanları değiştirme veya kaydı başka hesaba taşıma
- DELETE: Başka kullanıcıların verilerini silme
Yetki kontrolünü sorgunun kapsamına dahil edin
En güvenli yaklaşım, kaydı önce genel bir sorguyla bulup sonra uygulama katmanında kontrol etmek yerine, kullanıcı veya kiracı kapsamını doğrudan sorguya eklemektir. Böylece yanlışlıkla başka bir nesnenin döndürülmesi zorlaşır.
Örneğin PHP ve PDO kullanan basit bir güncelleme işleminde, yalnızca kaydın sahibi olan kullanıcı değişiklik yapabilmelidir:
$sql = 'UPDATE documents
SET title = :title, body = :body
WHERE id = :id AND owner_id = :owner_id';
$stmt = $pdo->prepare($sql);
$stmt->execute([
':title' => $input['title'],
':body' => $input['body'],
':id' => $documentId,
':owner_id' => $authenticatedUserId,
]);
if ($stmt->rowCount() !== 1) {
// Kayıt yok veya kullanıcı bu kayda yetkili değil.
http_response_code(404);
exit;
}
Buradaki `authenticatedUserId` değeri istek gövdesinden, URL'den veya gizli bir form alanından alınmamalıdır. Kimlik, doğrulanmış oturum ya da imzalı erişim belirtecinden sunucu tarafında elde edilmelidir. SQL sorguları parametreli hazırlanmalı; kullanıcı girdisi birleştirilerek sorgu oluşturulmamalıdır.
Çok kiracılı sistemlerde kapsam genellikle `tenant_id` ile genişletilir:
WHERE id = :id
AND tenant_id = :tenant_id
AND deleted_at IS NULL
`tenant_id` istemcinin gönderdiği değerden alınmamalı, kullanıcının oturumundaki aktif kiracı ve sunucu tarafındaki üyelik kontrolüyle belirlenmelidir.
Rol, sahiplik ve alan bazlı yetkilendirme
“Giriş yapmış olmak” bir kayda erişim için yeterli değildir. Her işlem için şu sorular açıkça yanıtlanmalıdır:
- Kullanıcı bu kaynağın sahibi mi?
- Kullanıcının rolü bu işlemi yapmaya izin veriyor mu?
- Kaynak, kullanıcının aktif kiracısına veya projesine bağlı mı?
- İşlem yapılacak alanlar için ek kural var mı?
$allowed = [
'display_name' => $input['display_name'] ?? null,
'timezone' => $input['timezone'] ?? null,
];
// role, user_id, tenant_id ve is_admin gibi alanları istemciden kabul etmeyin.
Yönetici rolleri için de sınırsız erişim varsayımı yerine ayrı bir politika tanımlanmalıdır. Destek personeli yalnızca belirli müşterileri görebilir, finans rolü ödeme alanlarını okuyabilir fakat kullanıcı profilini değiştiremeyebilir.
API tasarımında ve istemci tarafında dikkat edilecek noktalar
Sunucu, URL'deki veya gövde içindeki kullanıcı kimliğine güvenmemelidir. Örneğin `/users/42/orders` isteğinde `42` değeri, oturumdaki kullanıcıyla veya yetkili yönetici kapsamıyla karşılaştırılmalıdır. Daha güvenli bir tasarım, normal kullanıcı işlemlerinde kullanıcı kimliğini sunucunun belirlemesi ve yalnızca yetkili yönetici uç noktalarında açık kimlik parametresi kullanılmasıdır.
JavaScript, mobil uygulama veya HTML arayüzünde butonu gizlemek yalnızca kullanıcı deneyimidir; güvenlik kontrolü değildir. İstek doğrudan HTTP istemcisiyle gönderildiğinde de aynı politika uygulanmalıdır. Ayrıca istemcide önbelleğe alınan yanıtlar, başka kullanıcı oturumlarına sızmayacak şekilde yapılandırılmalıdır.
Yetkisiz bir kaynağın varlığını açığa çıkarmamak için bazı API'ler hem “kayıt yok” hem de “erişim yok” durumunda 404 döndürür. Bu tercih tehdit modeline göre yapılmalıdır. 403 kullanılacaksa yanıt gövdesi başka kullanıcıların kayıtlarının varlığı hakkında gereksiz bilgi vermemelidir.
Test edilebilir bir güvenlik kontrol listesi
Her CRUD uç noktası için en az şu testleri yazın:
- Kullanıcı A, kendi kaydını okuyabiliyor ve güncelleyebiliyor mu?
- Kullanıcı A, kullanıcı B'nin kaydını ID değiştirerek okuyamıyor mu?
- Kullanıcı A, B'nin kaydını güncelleyemiyor veya silemiyor mu?
- Aynı işlem farklı bir `tenant_id` ile gönderildiğinde engelleniyor mu?
- Yetkisiz alanlar gönderildiğinde yok sayılıyor ya da reddediliyor mu?
- Kaynak kimliği bulunamadığında ve erişim olmadığında beklenen durum kodu dönüyor mu?
- Toplu listeleme uç noktası yalnızca yetkili kapsamı döndürüyor mu?
1. Kullanıcı A ile oturum aç.
2. A'ya ait bir belge oluştur ve kimliğini kaydet.
3. Kullanıcı B ile oturum aç.
4. B'nin A'nın belge kimliğine GET, PATCH ve DELETE göndermesini dene.
5. Her isteğin reddedildiğini ve belgenin değişmediğini doğrula.
6. B'nin listeleme yanıtında A'nın belgesinin bulunmadığını kontrol et.
Son olarak erişim kararlarını merkezi politika veya servislerde toplamak, aynı kontrolün farklı controller'larda unutulmasını önler. Loglarda kullanıcı, işlem, kaynak türü ve sonuç tutulabilir; ancak erişim belirteci, parola, kişisel veri ve hassas istek gövdeleri kaydedilmemelidir. Yetkilendirme kuralı kodla birlikte gözden geçirilmeli ve her yeni CRUD uç noktası için hem olumlu hem de negatif test eklenmelidir.