Skip to main content

Welcome to a Grasp lesson

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

Lesson 21

Durum Makineleriyle İş Akışı Modelleme

Recommended resources

Merhaba, bir önceki dersimizde ORM sorguları ve veri erişim katmanlarını inceleyerek çok kiracılı (multi-tenant) bir mimarideki veri izolasyonu zafiyetlerini nasıl tespit edeceğimizi öğrendik. Bu dersin odak noktası, bir kullanıcının hangi veriye erişebileceği, yani veri görünürlüğü idi.

Bu dersimizde ise perspektifimizi değiştiriyoruz. Artık tekil fonksiyonlardaki izin kontrollerinden veya veri filtrelerinden ziyade, bir bütün olarak uygulamanın davranışsal akışına odaklanacağız. Konumuz, İş Mantığı Zafiyetleri. Bu kapsamda, uygulama iş akışlarını koddan durum makineleri olarak modelleyerek, bir sürecin adımlarını atlamaya, sırasını değiştirmeye veya geçersiz bir duruma zorlamaya olanak tanıyan kritik mantık hatalarını nasıl bulacağımızı ele alacağız. Bu, genellikle otomatik araçların gözden kaçırdığı, ancak bir uygulamanın temel güvenliğini baltalayabilen bir zafiyet sınıfıdır.

Durum Makineleri: Uygulama Akışının Zihinsel Modeli

Matematik ve bilgisayar bilimleri temellerinizden de aşina olabileceğiniz gibi, bir durum makinesi (state machine), bir sistemin bulunabileceği sonlu sayıdaki durumları ve bu durumlar arasındaki geçişleri tanımlayan bir modeldir. Güvenli kod analizi bağlamında, bu soyut kavramı, bir uygulamanın iş akışını (örneğin bir siparişin yaşam döngüsü) zihinsel olarak haritalandırmak için güçlü bir araç olarak kullanırız.

Bir durum makinesinin temel bileşenleri şunlardır:

  • Durumlar (States): Bir nesnenin veya sürecin içinde bulunabileceği belirli aşamalar. Örneğin, bir sipariş için PENDING, PAID, SHIPPED, CANCELLED gibi durumlar.
  • Geçişler (Transitions): Bir durumdan diğerine geçişi tetikleyen eylem veya olaylar. Örneğin, process_payment eylemi, siparişi PENDING durumundan PAID durumuna geçirir.
  • Geçiş Koşulları (Guard Conditions): Bir geçişin gerçekleşebilmesi için sağlanması gereken mantıksal kurallar. Örneğin, bir siparişin SHIPPED durumuna geçebilmesi için order.status == 'PAID' ve user.is_staff() koşullarının doğru olması gerekir.
  • Girdi Eylemleri (Entry/Exit Actions): Bir duruma girerken veya bir durumdan çıkarken çalışan kod. Örneğin, SHIPPED durumuna girildiğinde müşteriye bir e-posta gönderilmesi.

Aşağıdaki görsel, bir kapının "açıldı" ve "kapandı" durumları arasındaki geçişleri gösteren basit bir durum makinesi diyagramıdır ve bu bileşenleri net bir şekilde özetler.

Bu basit diyagramda "açıldı" ve "kapandı" iki durumu, "kapıyı_aç" ve "kapıyı_kapa" ise bu durumlar arasındaki geçişleri temsil eder. Her geçiş bir eylemdir ve bir koşula bağlanabilir.

Bu basit diyagramda "açıldı" ve "kapandı" iki durumu, "kapıyı_aç" ve "kapıyı_kapa" ise bu durumlar arasındaki geçişleri temsil eder. Her geçiş bir eylemdir ve bir koşula bağlanabilir.

Bu zihinsel modeli kullanarak bir uygulamanın iş akışını analiz ettiğimizde, amacımız "beklenmeyen geçişleri" bulmaktır. Bu konunun sızma testi pratiklerindeki önemini anlamak için aşağıdaki kaynağın ilgili bölümüne göz atalım.

Web Uygulaması Sızma Testi Checklist (OWASP) | SİTEY Blog

Bu sızma testi kontrol listesi, iş mantığı zafiyetlerinin önemini ve manuel analizin gerekliliğini vurguluyor. Bu, bizim uzman odaklı kod analizi hedefimizle doğrudan örtüşmektedir.

Lütfen belgede "İş Mantığı ve Akışlar" başlığı altındaki kısa bölümü okuyun. Özellikle "Durum makinelerinde beklenmeyen geçişler" ifadesine dikkat edin. Ardından, SSS bölümündeki "Otomasyonla neleri yakalayamam?" sorusunu ve cevabını inceleyin. Bu bölüm, bu derste ele aldığımız gibi bağlamsal akış bozukluklarının neden manuel keşif gerektirdiğini açıklamaktadır.


İş Akışını Koddan Çıkarma ve Zafiyet Avı

Güvenli kod denetimindeki görevimiz, uygulamanın durum makinesini koddan tersine mühendislikle çıkarmak ve ardından bu modelde mantıksal boşluklar aramaktır.

1. Adım: Durumları ve Geçişleri Tanımlama

Bir iş akışının durum makinesini çıkarmak için kod tabanında aşağıdaki ipuçlarını arayın:

  • Veritabanı Modelleri: Order, Shipment, Document gibi modellerde status, state veya benzeri bir alan var mı? Bu alan genellikle sürecin mevcut durumunu tutar.
  • Sabitler ve Enum'lar: Kodda STATUS_CHOICES, OrderState gibi sabit veya enum tanımları, olası tüm durumların bir listesini verir. Bu, haritanızın temelini oluşturur.
  • Controller'lar ve Servisler: Durum alanını güncelleyen fonksiyonları veya metodları bulun. Bunlar sizin geçişlerinizdir. Örneğin, bir approve_document endpoint'i, Document modelinin durumunu PENDING_APPROVAL'dan APPROVED'a değiştirir.

2. Adım: Yasadışı Geçişleri Tespit Etme

Durumları ve geçişleri kabaca haritalandırdıktan sonra, asıl av başlar: koddaki eksik veya zayıf geçiş koşullarını (guard conditions) bularak yasadışı durum geçişlerine zorlamak.

Aşağıda bir e-ticaret uygulamasının sipariş akışını ele alalım:

Olası Durumlar: PENDING (Ödeme bekleniyor), PAID (Ödendi), SHIPPED (Kargolandı), CANCELLED (İptal edildi).

Güvenlik Soruları:

  1. Adım Atlama (Workflow Bypass): Bir sipariş PAID durumundan geçmeden doğrudan PENDING'den SHIPPED'a geçirilebilir mi? Bu, ücretsiz ürün almak anlamına gelir.
  2. Durum Geriletme (State Reversion): CANCELLED durumundaki bir sipariş (ve parası iade edilmiş olabilir) tekrar SHIPPED durumuna alınabilir mi?
  3. Yetkisiz Geçiş: Bir siparişi SHIPPED durumuna geçiren fonksiyon (ship_order) sadece personel tarafından mı çağrılabiliyor? Bu fonksiyonun içinde hem yetki hem de mevcut durum kontrolü yapılıyor mu?

Şimdi bu senaryoları kodda arayalım. Aşağıda, adım atlama zafiyetini içeren bir Python/Django kodu örneği bulunmaktadır.

# ZAFİYETLİ KOD ÖRNEĞİ

# models.py
class Order(models.Model):
    STATUS_CHOICES = [
        ('PENDING', 'Pending Payment'),
        ('PAID', 'Paid'),
        ('SHIPPED', 'Shipped'),
        ('CANCELLED', 'Cancelled'),
    ]
    status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='PENDING')
    # ... diğer alanlar

# views.py
@staff_member_required # Django'nun bu decorator'ı sadece personel erişimini kontrol eder.
def ship_order_view(request, order_id):
    order = get_object_or_404(Order, pk=order_id)
    
    # ZAFİYET: Siparişin mevcut durumu kontrol edilmiyor.
    # Eğer siparişin durumu 'PENDING' ise, yani ödeme yapılmamışsa bile,
    # yetkili bir personel (belki de kötü niyetli veya hesabı ele geçirilmiş)
    # bu endpoint'i çağırarak siparişi kargolanmış durumuna geçirebilir.
    order.status = 'SHIPPED'
    order.save()
    
    send_shipping_notification(order)
    return HttpResponse("Order has been shipped.")

Bu zafiyeti düzeltmek, geçiş fonksiyonuna bir durum kontrolü (guard condition) eklemek kadar basittir:

# GÜVENLİ KOD ÖRNEĞİ

@staff_member_required
def ship_order_view(request, order_id):
    order = get_object_or_404(Order, pk=order_id)
    
    # DÜZELTME: Mevcut durum kontrolü eklendi.
    # Geçişe sadece 'PAID' durumundan izin verilir.
    if order.status != 'PAID':
        return HttpResponseBadRequest("Order cannot be shipped. Payment not completed.")
        
    order.status = 'SHIPPED'
    order.save()
    
    send_shipping_notification(order)
    return HttpResponse("Order has been shipped.")

Bu tür iş akışı atlatma zafiyetleri, pratikte en sık karşılaşılan ve etkisi en yüksek mantık hatalarındandır. OWASP kontrol listesi de bu konuya özellikle dikkat çeker.

Web Uygulaması Sızma Testi Checklist (OWASP) | SİTEY Blog

Bu bölüm, iş mantığı testlerinin pratik senaryolarını özetleyerek konuyu somutlaştırıyor.

Belgedeki "İş Mantığı Testleri" başlığı altındaki ilk paragrafı okuyun. Ardından, "İş akışı bypass" alt başlığını dikkatlice inceleyin. Burada bahsedilen "ödeme adımını atlayarak sipariş tamamlama" senaryosu, yukarıda analiz ettiğimiz kod zafiyetinin tam olarak karşılığıdır.


Durum Yönetimini Merkezileştirmenin Önemi

Her geçiş fonksiyonuna manuel olarak if order.status == ... kontrolü eklemek, insan hatasına açık bir yaklaşımdır. Geliştiriciler bu kontrolü kolayca unutabilir. Daha olgun ve güvenli bir mimari, durum geçiş mantığını merkezileştirir.

Bunu yapmanın yollarından biri, durumu doğrudan değiştirmek yerine, durumu değiştiren metodlar kullanmaktır.

# MERKEZİLEŞTİRİLMİŞ DURUM YÖNETİMİ ÖRNEĞİ

class Order(models.Model):
    # ... status alanı ve diğer alanlar ...

    def pay(self):
        if self.status != 'PENDING':
            raise InvalidTransitionError("Order is not pending payment.")
        self.status = 'PAID'
        # Ödeme sonrası eylemler...

    def ship(self):
        if self.status != 'PAID':
            raise InvalidTransitionError("Order must be paid before shipping.")
        self.status = 'SHIPPED'
        # Kargo sonrası eylemler...

# views.py
@staff_member_required
def ship_order_view(request, order_id):
    order = get_object_or_404(Order, pk=order_id)
    try:
        # Artık durumu doğrudan değiştirmiyoruz, geçiş metodunu çağırıyoruz.
        # Geçiş kuralları modelin içinde kapsüllenmiş durumda.
        order.ship() 
        order.save()
    except InvalidTransitionError as e:
        return HttpResponseBadRequest(str(e))
    
    return HttpResponse("Order has been shipped.")

Bu deseni kodda gördüğünüzde, analiz odağınız değişir. Artık her endpoint'i tek tek kontrol etmek yerine, bu merkezi geçiş metodlarını (pay, ship, cancel vb.) ve içerdikleri mantığı denetlersiniz. Bu, denetim sürecini çok daha verimli ve güvenilir hale getirir. Birçok framework ve kütüphane (django-fsm gibi) bu tür durum makinesi mantığını uygulamak için hazır çözümler sunar. Kodda böyle bir kütüphane kullanımı görürseniz, denetiminizi bu kütüphanenin nasıl yapılandırıldığına odaklamalısınız.


Sonuç

Bu derste, uygulama güvenliğinin en karmaşık ve bağlama duyarli alanlarından biri olan iş mantığı zafiyetlerini nasıl tespit edeceğimizi öğrendik. Bir uygulamanın iş akışını durum makinesi olarak modelleme tekniği, bu zafiyetleri sistematik bir şekilde bulmak için güçlü bir zihinsel araçtır.

Temel Çıkarımlar:

  • Model Oluşturun: Kod analizi yaparken ilk adım, status alanlarını, enum'ları ve durumu değiştiren fonksiyonları bularak uygulamanın zihinsel durum makinesi modelini oluşturmaktır.
  • Geçişleri Sorgulayın: Her durum geçişi için "Bu geçişe izin veren koşul (guard) nedir?" ve "Bu koşul atlatılabilir mi?" sorularını sorun.
  • Yaygın Hataları Arayın: Adım atlama (workflow bypass), geçersiz durumlara geri dönme ve terminal durumlar üzerinde işlem yapma gibi klasik iş mantığı hatalarına odaklanın.
  • Mimarinin Olgunluğunu Değerlendirin: Güvenli mimariler, durum geçişlerini merkezileştirerek geliştirici hatalarını en aza indirir. Kodda dağınık if status == ... kontrolleri yerine merkezi geçiş metodları veya FSM kütüphaneleri arayın.

Bu dersimizde olayların doğru sırayla gerçekleşmesini sağladık. Bir sonraki dersimizde ise olayların doğru zamanlamayla gerçekleşmesine odaklanacağız. Paylaşılan kaynaklara aynı anda erişmeye çalışan birden fazla isteğin veya iş parçacığının, sıralı analizde mükemmel görünen güvenlik kontrollerini nasıl atlatabileceğini inceleyeceğiz. Konumuz, çok iş parçacıklı ve asenkron kodda yarış koşulları (race conditions) olacak.