Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 34

Örtük Bağlama ve DTO'larda Mass Assignment Zafiyetleri

Recommended resources

Önceki dersimizde, serileştirilmiş verilerden nesneler oluşturulurken tetiklenen ve readObject gibi belirgin fonksiyonlarla kendini gösteren "açık" deserialization zafiyetlerini ve bu zafiyetlerin gücünü ortaya çıkaran gadget zincirlerini derinlemesine inceledik. Bir saldırganın, mevcut kod parçalarını bir araya getirerek nasıl keyfi bir yürütme akışı "programlayabildiğini" gördük.

Bu dersimizde ise, modern uygulama geliştirmenin ayrılmaz bir parçası olan ve çok daha gizli bir şekilde karşımıza çıkan bir deserialization türüne odaklanacağız. MVC (Model-View-Controller) ve API framework'leri, gelen HTTP isteklerindeki JSON veya form verilerini otomatik olarak kodumuzdaki nesnelere (Modeller, DTO'lar) dönüştürür. Bu "örtük nesne bağlama" (implicit object binding) mekanizması, geliştiriciye büyük kolaylık sağlarken, dikkatli incelenmediğinde Mass Assignment (Toplu Atama) adı verilen kritik zafiyetlere kapı aralar. Bu dersin sonunda, bu örtük dönüşüm zincirlerindeki gizli mass assignment zafiyetlerini bir uzman gözüyle nasıl tespit edeceğini öğreneceksin.

Mass Assignment: Framework Büyüsü ve Gizli Tehlike

Mass assignment, bir web framework'ünün, bir HTTP isteği gövdesinde (örneğin bir JSON payload'ı) bulunan anahtar-değer çiftlerini, herhangi bir filtreleme veya ayrım yapmaksızın doğrudan bir nesnenin özelliklerine (properties) atamasıyla ortaya çıkar. Geliştirici sadece email ve password alanlarının güncellenmesini beklerken, saldırgan tarafından isteğe eklenen isAdmin: true gibi bir alan, nesnenin güvenlik açısından kritik bir özelliğini değiştirebilir.

Senin de uygulama güvenliği ve sızma testi alanındaki tecrübenden bildiğin gibi bu, OWASP'ın API Security Top 10 listesinde yer alan Kırık Nesne Özellik Seviyesi Yetkilendirme (Broken Object Property Level Authorization - BOPLA) zafiyetinin en yaygın tezahürlerinden biridir.

Aşağıdaki görsel, bu konsepti mükemmel bir şekilde özetliyor. Bir PATCH isteği ile gönderilen ve normalde kullanıcı tarafından değiştirilmemesi gereken gizli bir is_admin özelliğinin nasıl manipüle edilebildiğini göstermektedir.

Bu görsel, bir saldırganın API isteğine eklediği `is_admin: true` gibi bir özellikle, nesnenin korunan özelliklerini nasıl değiştirebildiğini ve yetki yükseltme sağlayabildiğini (Mass Assignment) veya beklenenden fazla veriyi nasıl ifşa edebildiğini (Excessive Data Exposure) gösteren bir BOPLA diyagramıdır.

Basit Desen: Güvenilen İstek Gövdesi ve Doğrudan Bağlama

En temel mass assignment zafiyeti, gelen istek gövdesinin içeriğine tamamen güvenilerek, doğrudan bir model oluşturma veya güncelleme fonksiyonuna geçirilmesiyle oluşur. Özellikle dinamik dillerin ve esnek ORM'lerin (Object-Relational Mapper) kullanıldığı framework'lerde bu desene sıkça rastlanır.

Aşağıdaki Node.js/Express kod parçacığı, bu tehlikeli deseni göstermektedir.

Bu kodda, `req.body` doğrudan bir değişkene atanıyor. Eğer `body` nesnesi, `User.create(body)` veya `user.update(body)` gibi bir ORM metoduna doğrudan verilseydi, bu bir mass assignment zafiyetine yol açabilirdi.

Bu kod parçasında, 31. satırdaki const body = req.body; ifadesi tek başına bir zafiyet değildir. Ancak, bu body nesnesinin daha sonra nasıl kullanıldığı kritiktir. Farz edelim ki devamında şöyle bir kod bulunsun:

// hypothetical continuation of the code
const user = await User.findByPk(req.params.id);
await user.update(body); // VULNERABLE CODE

Burada sequelize.update() gibi bir ORM metodu, body nesnesindeki tüm anahtarları alıp user modelinin ilgili sütunlarına atamaya çalışacaktır. Eğer User modelinin veritabanında role veya account_balance gibi sütunları varsa, saldırgan şu şekilde bir JSON payload göndererek bu değerleri keyfi olarak değiştirebilir:

{
  "daily_exercises": [60, 60, 60],
  "target": 180,
  "role": "admin",
  "account_balance": 999999
}

Kod denetimi sırasında bu tür update(req.body) veya create(req.body) desenleri, derhal alarm zillerini çaldırması gereken en bariz kırmızı bayraklardır.


İleri Seviye Tespiti: DTO Dönüşüm Zincirleri ve Gizli Atamalar

Modern ve karmaşık uygulamalarda, zafiyet genellikle yukarıdaki kadar bariz değildir. Genellikle, gelen istek verileri önce bir Veri Transfer Nesnesi'ne (DTO - Data Transfer Object), ardından bu DTO bir veya daha fazla ara katmandan geçerek en sonunda veritabanı Varlık (Entity) nesnesine dönüştürülür. Zafiyet, bu dönüşüm zincirinin bir halkasında gizlenir.

Bu senaryoda kod denetçisinin görevi, bu DTO-Entity dönüşümünü gerçekleştiren mantığı bulup analiz etmektir. Tehlike genellikle, geliştiricinin işini kolaylaştırmak için kullandığı genel amaçlı "nesne eşleştirici" (object mapper) kütüphanelerinde yatar.

Örnek Senaryo: Java/Spring'de Gizli Mass Assignment

Bir kullanıcının profilindeki displayName ve bio bilgilerini güncelleyebildiği bir endpoint düşünelim.

1. Veritabanı Varlığı (UserEntity.java)
Bu sınıf, veritabanındaki users tablosunu temsil eder ve hassas alanlar içerir.

@Entity
public class UserEntity {
    @Id
    private Long id;
    private String username;
    private String passwordHash;
    private String displayName;
    private String bio;
    private String role; // Hassas alan!
    private boolean isActive; // Hassas alan!

    // Getters and setters...
}

2. Veri Transfer Nesnesi (UserUpdateDTO.java)
Bu sınıf, API üzerinden sadece güncellenmesine izin verilen alanları tanımlamak için kullanılır.

public class UserUpdateDTO {
    private String displayName;
    private String bio;

    // Getters and setters...
}

3. Kontrolcü (Controller) ve Servis Katmanı (Vulnerable Version)
Geliştirici, DTO'dan Entity'ye dönüşüm için Apache Commons BeanUtils gibi bir kütüphane kullanıyor.

// In a @Service class
public void updateUserProfile(Long userId, UserUpdateDTO dto) {
    UserEntity user = userRepository.findById(userId).orElseThrow();

    // TEHLİKELİ KOD: BeanUtils.copyProperties
    // Bu metot, kaynak (dto) ve hedef (user) nesnelerindeki
    // aynı isme sahip tüm property'leri kopyalamaya çalışır.
    try {
        BeanUtils.copyProperties(user, dto); 
    } catch (Exception e) {
        // ...
    }

    userRepository.save(user);
}

İlk bakışta kod güvenli görünüyor. UserUpdateDTO sadece displayName ve bio alanlarını içeriyor. Peki zafiyet nerede?

BeanUtils.copyProperties gibi kütüphaneler, property'leri isimlerine göre eşleştirir. Saldırgan, aşağıdaki gibi bir istek gönderirse ne olur?

PUT /api/users/123

{
  "displayName": "Hacker",
  "bio": "I own this now",
  "role": "ADMIN",
  "class": { 
      "classLoader": {}
  }
}

BeanUtils.copyProperties (ve benzeri birçok mapper), dto nesnesinden getRole() metodunu çağırmaya çalışır. Çoğu dilde, bu metot olmadığı için bir hata fırlatılır ve işlem durur. Ancak, bazı kütüphanelerin ve dillerin (özellikle Groovy gibi dinamik JVM dilleri) çalışma şekli, gelen JSON'dan dinamik olarak özellikler oluşturabilir.

Daha da tehlikelisi, class gibi özel bir anahtar kelime kullanmaktır. BeanUtils.copyProperties gibi bazı eski kütüphaneler, dto.getClass() metodunu çağırarak nesnenin sınıf tanımına erişmeye çalışabilir. Saldırgan, JSON'da class anahtarını manipüle ederek class.classLoader gibi zincirleme özelliklere erişmeyi deneyebilir ve bu durum, önceki derste gördüğümüz gadget zincirlerine benzer şekilde beklenmedik kod yürütme yollarına (örneğin RCE) yol açabilir. Bu, "Property-Oriented Programming" saldırılarının mass assignment ile nasıl kesişebileceğine dair ileri seviye bir örnektir.


Kod Denetimi Stratejileri

Mass assignment zafiyetlerini sistematik olarak bulmak için şu adımları izleyebilirsin:

  1. Veri Değiştiren Endpoint'leri Belirle: Kaynak kodda POST, PUT, PATCH request'lerini işleyen tüm controller metotlarını veya route handler'larını listele.
  2. Giriş Noktasını Analiz Et: Bu endpoint'ler, isteği hangi nesneye veya veri yapısına bağlıyor? (@RequestBody UserUpdateDTO dto), req.body gibi ifadeleri ara.
  3. Veri Akışını İzle: Bu giriş nesnesinden, veritabanına yazılan son Varlık (Entity) nesnesine kadar olan veri akışını takip et. Veri hangi dönüşümlerden geçiyor?
  4. Eşleştirme (Mapping) Mantığını Mercek Altına Al:
    • Kırmızı Bayraklar: BeanUtils.copyProperties, ModelMapper.map, object.setAttributes(params) gibi genel amaçlı, "örtük" eşleştirme fonksiyonlarını ara. Bu fonksiyonlar, genellikle bir kara kutu gibi çalışır ve beklenmedik atamalara neden olabilir.
    • Güvenli Desenler: Alanların tek tek, "açıkça" atandığı kodları ara. Örneğin: user.setDisplayName(dto.getDisplayName()); user.setBio(dto.getBio());. Bu yöntem, hangi alanların değiştirilebileceğini net bir şekilde tanımladığı için çok daha güvenlidir.
  5. DTO ve Entity Sınıflarını Karşılaştır: DTO'da bulunmayan ancak Entity'de bulunan hassas alanları (role, is_active, balance, owner_id vb.) belirle. Zafiyet potansiyeli bu farkta yatar. Mapper'ın bu hassas alanları doldurmasına izin veren bir yol var mı?

Sonuç ve Sonraki Adımlar

Bu derste, modern framework'lerin sunduğu otomasyonun nasıl gizli güvenlik riskleri yaratabileceğini gördük. Mass assignment, basit bir isAdmin=true enjeksiyonundan, DTO dönüşüm zincirlerindeki karmaşık ve gizli atamalara kadar geniş bir yelpazede karşımıza çıkabilir.

Anahtar Çıkarımlar:

  • Örtük Deserialization: Framework'lerin model bağlama mekanizması, isteği nesneye dönüştüren örtük bir deserialization sürecidir ve bu süreç, zafiyetin kaynağıdır.
  • Tehlikeli Kolaylık: BeanUtils.copyProperties gibi genel amaçlı nesne eşleştiriciler, hangi alanların kopyalanacağı üzerinde hassas kontrol sağlamadıkları için tehlikelidir.
  • Güvenli Alternatif: En güvenli yaklaşım, alanları DTO'dan Entity'ye manuel ve açık bir şekilde (alan-alan) kopyalamaktır. Bu, hangi verinin nereye gittiği üzerinde tam kontrol sağlar.
  • Denetim Odağı: Kod denetiminde odak noktası, veri akışını API sınırından veritabanı modeline kadar izlemek ve bu yoldaki tüm "eşleştirme" veya "dönüşüm" mantığını sorgulamak olmalıdır.

Bu derste, bir nesne örneğinin özelliklerini nasıl manipüle edebileceğimizi inceledik. Bir sonraki adımda, konsepti daha da ileri taşıyarak, sadece tek bir nesneyi değil, o nesnenin ait olduğu sınıfın kendisini nasıl hedef alabileceğimizi göreceğiz. Özellikle JavaScript gibi dinamik ve prototip tabanlı dillerde, nesne birleştirme operasyonları, Prototype Pollution adı verilen ve tüm uygulama genelinde etki yaratabilen yıkıcı bir zafiyete yol açabilir. Bir sonraki dersimizde, bu zafiyetin ardındaki mantığı ve kodda nasıl tespit edileceğini ele alacağız.