İçeriğe geç
Sedat Özdemir
Yazılar

business-logic

Parametrelerin Sessiz Çığlığı: Modern Web Uygulamalarında Logic ve Mimari Avcılığı

Modern web uygulamalarında artık XSS veya basit SQLi bulmak samanlıkta iğne aramaya benziyor. Gerçek tehlike, uygulamanın iş mantığında ve API tasarımındaki mimari açıklarda gizli.

Sedat Özdemir
· 4 dk okuma

POST /api/v1/order/checkout HTTP/1.1 isteğindeki 'amount' parametresini -1 ile çarpıp gönderdiğinde sunucu sana 200 OK dönüyorsa, o an tüm güvenlik mimarin kağıt üzerinde kalmış demektir. Geçmiş olsun, artık sadece bir yazılımcı değil, bir 'finansal risk' haline geldin.

Selamlar millet, ben Sedat. Bugün sahada, özellikle modern mikroservis mimarilerinde başımızı en çok ağrıtan, otomatize araçların çoğunlukla ıskaladığı ama bir Red Teamer olarak benim iştahımı en çok kabartan konuya dalıyoruz: Business Logic (İş Mantığı) hataları ve gelişmiş IDOR senaryoları. Kahveniz hazırsa, 'testCompany' üzerindeki hayali (ama çokça yaşanmış) senaryolarımızla başlayalım.

IDOR: Sadece Sayı Değiştirmekten Çok Daha Fazlası

Çoğu kişi IDOR'u (Insecure Direct Object Reference) hala id=123 olan yeri id=124 yapmak sanıyor. Arkadaşlar, sene 2024. Artık karşımızda UUID'ler, hashlenmiş ID'ler ve JWT'ler var. Ama bu, IDOR'un bittiği anlamına mı geliyor? Kesinlikle hayır. Sadece evrim geçirdi.

Örneğin, bir profil güncelleme isteği düşünün: PUT /api/users/me/settings Payload: {"email": "[email protected]", "role": "USER"}

Burada saldırganın ilk deneyeceği şey role parametresini ADMIN yapmaktır. Eğer backend tarafında bu parametre 'mass assignment' yöntemiyle direkt veritabanına basılıyorsa, tebrikler; tek bir request ile yetki yükselttiniz (Privilege Escalation).

Defansif Reçete: DTO (Data Transfer Object) kullanın. Kullanıcıdan gelen her veriyi direkt veritabanı modeline map etmeyin. Hangi alanların güncellenebilir olduğunu beyaz liste (allowlist) ile belirleyin.

SSRF: İçerideki Truva Atı

Server-Side Request Forgery (SSRF), modern cloud (AWS, Azure, GCP) dünyasının en tehlikeli açıklarından biri. Uygulamanızın dışarıdan bir URL alıp oraya istek attığı her nokta (profil resmi yükleme, URL önizleme, webhook tanımları) potansiyel bir bombadır.

Genellikle gördüğümüz hata şu: GET /generate-pdf?url=http://example.com/invoice/1

Bir saldırgan bunu şu şekilde manipüle ederse ne olur? GET /generate-pdf?url=http://127.0.0.1:80/admin/status

Veya bir bulut ortamındaysanız, metadata servislerine erişmeye çalışır: GET /generate-pdf?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/

Buradan sızacak bir IAM rolü, tüm altyapınızı saldırganın kontrolüne verebilir.

Zararsızlaştırılmış Payload Örneği:

# Metadata servisinden AWS credential çekme denemesi (Defanged)
curl -X GET "http://example.com/proxy?url=http://169.254.169.254/latest/meta-data/"

Nasıl Engelleriz?

  1. URL parsing işlemini düzgün yapın. Sadece izin verilen domainlere (allowlist) izin verin.
  2. Dahili IP bloklarına (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 ve loopback) erişimi uygulama seviyesinde ve network katmanında yasaklayın.

Business Logic: Kodun Mantıksızlaştığı An

İş mantığı hataları, yazılımcının "Kullanıcı zaten bunu yapmaz" dediği anda başlar. Favori senaryom: Race Condition (Yarış Durumu).

Bir e-ticaret sitesinde (testCompany üzerinden düşünelim) hediye kuponu kullandığınızı varsayalım. APPLY_COUPON butonuna aynı anda 50 farklı thread ile istek atarsanız ne olur? Eğer backend tarafında bakiye düşümü veya kupon kullanımı 'atomic' bir işlem değilse, 100 TL'lik kuponu 10 defa kullandırıp 1000 TL bakiye elde edebilirsiniz.

Pseudo-kod ile Hatalı Mantık:

# ZAFİYETLİ KOD
def apply_coupon(user_id, coupon_code):
    coupon = db.find_coupon(coupon_code)
    if coupon.is_valid:
        user.balance += coupon.value
        # Tam bu noktada diğer thread araya girerse...
        coupon.mark_as_used()

Çözüm: Veritabanı seviyesinde lock (kilit) mekanizmaları kullanın veya işlemleri atomik hale getirin. Redis gibi hızlı cache yapılarında 'distributed lock' yöntemlerine başvurun.

JWT (JSON Web Token) Sefaleti

Herkes JWT kullanıyor ama kimse içindeki 'alg' parametresine bakmıyor. alg: none saldırısı tarih oldu diyebilirsiniz ama hala kütüphane bazlı öyle açıklar çıkıyor ki şaşırırsınız. Ayrıca JWT'nin payload kısmına hassas veri koymak, anahtarı kapının üstünde unutmak gibidir. JWT şifreli değil, sadece imzalıdır (JWE kullanmıyorsanız).

Bir saldırgan olarak ben ne yaparım? JWT içindeki kullanıcı ID'sini değiştirip, imza kısmını (signature) bozup sunucunun bunu doğrulamadan kabul edip etmediğini kontrol ederim.

// Zararsızlaştırılmış JWT Payload örneği
{
  "alg": "none", // Bazı hatalı konfigürasyonlarda sunucu imzayı kontrol etmez
  "typ": "JWT"
}
// Payload
{
  "user_id": "1",
  "role": "admin"
}

Ne Yapmalı? (Savunma Hattı)

Güvenlik, sadece bir tool'u (Pentest tool veya DAST) çalıştırıp rapor almak değildir. Biz Red Team olarak sistemin mantığındaki çatlakları ararız. Sizin de yapmanız gerekenler şunlar:

  1. Tehdit Modelleme: Kod yazılmadan önce "Bu özellik nasıl kötüye kullanılabilir?" diye sorun.
  2. Input Validation Değil, Input Sanitization & Type Checking: Gelen verinin sadece tipini değil, bağlamını da kontrol edin.
  3. Logging & Monitoring: Olağandışı miktarda 403 (Forbidden) veya 404 dönen bir kullanıcı varsa, muhtemelen bir şeyleri enumerate ediyordur. Onu izleyin.
  4. Least Privilege: Uygulamanın veritabanı kullanıcısı bile 'root' olmamalı. Sadece ihtiyacı olan tablolara, ihtiyacı olan yetkiyle erişmeli.

Web güvenliği bir varış noktası değil, bir yolculuktur arkadaşlar. Bugün kapattığınız bir açık, yarın yeni bir teknolojiyle tekrar hortlayabilir. O yüzden kod yazarken her zaman 'paranoyak' olun, ama bu paranoya sizi geliştiren bir disipline dönüşsün.

Bir sonraki yazıda API güvenliğinin derinliklerine, özellikle GraphQL ve kafa karıştıran yetkilendirme modellerine dalacağız. O zamana kadar loglarınıza iyi bakın, her 200 OK'e de hemen güvenmeyin.

İlgili yazılar