Hızlı cevap: Race condition (yarış koşulu), iki veya daha fazla isteğin aynı anda aynı veri üzerinde çalışması yüzünden uygulamanın tek seferlik olması gereken bir işlemi birden çok kez yapmasıdır. Klasik kalıp TOCTOU'dur (time-of-check to time-of-use): uygulama bir koşulu kontrol eder (kupon kullanılmamış, bakiye yeterli) ama kontrol ile işlem arasında geçen kısacık sürede ikinci bir istek aynı kontrolü geçer. Sonuç limit-overrun'dır: tek kupon iki kez, tek bakiye iki çekim, tek davet on hesap. Savunma istemci tarafında değil veritabanı tarafındadır: atomik koşullu güncelleme (tek UPDATE ile hem kontrol hem yazma), satır kilidi (SELECT FOR UPDATE), benzersizlik kısıtı ve idempotency anahtarı. Saldırganlar bunu Burp'ün tek-paket (single-packet) tekniğiyle milisaniye hizasında tetikler.

Race condition açıkları en sinsi web zafiyetlerindendir çünkü kod tek başına okununca doğru görünür: kontrol var, yazma var, mantık yerinde. Hata yalnızca iki istek milisaniyeler içinde çakıştığında ortaya çıkar ve bu yüzden normal testte neredeyse hiç görünmez. Bu rehber yarış koşulunun gerçekte nasıl çalıştığını, en sık görülen limit-overrun örneklerini, saldırganın bunu nasıl güvenilir biçimde tetiklediğini ve doğru savunmanın neden her zaman veri katmanında olması gerektiğini uzman bakışıyla anlatır.

TOCTOU: kontrol ile kullanım arasındaki boşluk

Yarış koşullarının çoğu TOCTOU kalıbına uyar. Uygulama önce bir durumu okur (check), sonra o duruma dayanarak bir iş yapar (use). Tek-iş-parçacıklı düşünen geliştirici için bu güvenlidir. Ama gerçek sunucu aynı anda onlarca isteği paralel işler. İki istek neredeyse aynı anda gelirse, ikisi de "kontrol" aşamasını eski durumda geçer, sonra ikisi de "kullanım" aşamasını yapar.

Somut örnek: bir hediye kuponu kullanildi = false ise kabul ediliyor. İstek A kuponu okur (false), istek B de okur (hâlâ false, çünkü A henüz yazmadı), ikisi de kuponu uygular, ikisi de kullanildi = true yazar. Tek kupon iki kez indirim yaptı. Aynı kalıp bakiye çekiminde çift-harcama (double spend), davet kodunda sınırsız davet, oy/beğeni sayacında şişirme, stok düşümünde negatif stok olarak çıkar.

Sorun kodun yanlış olması değil, kontrol ile yazmanın atomik olmamasıdır. Araya başka bir işlem girebiliyorsa, girecektir.

En sık görülen limit-overrun senaryoları

Senaryo Beklenen Yarış koşulu sonucu
Tek kullanımlık kupon/indirim 1 kez uygulanır N paralel istekle N kez
Para/puan çekimi Bakiye kadar Bakiyeden fazla çekim (double spend)
Davet/referans kodu Sınır kadar davet Sınırın çok üstünde
Stok/rezervasyon Stok kadar satış Fazla satış, negatif stok
Oy, beğeni, anket Kişi başı 1 Şişirilmiş sayaç
2FA/OTP deneme sınırı 5 deneme Paralel isteklerle sınır aşımı
Hesaba para yükleme/çekme Tek işlem Çift kayıt

Ortak payda hep aynıdır: "bir şey en fazla N kez olmalı" kuralının, paralel isteklerle N'in üstüne çıkması. Bu yüzden bu sınıf iş-mantığı testinin ayrılmaz parçasıdır ve otomatik tarayıcılar çoğu zaman kaçırır; doğru tespit için uygulamayı anlayan bir web uygulaması sızma testi gerekir.

Saldırgan bunu nasıl tetikler: tek-paket saldırısı

Eskiden yarış koşulunu tetiklemek için istekleri aynı anda göndermek ağ gecikmesi (jitter) yüzünden zordu. Modern teknik bunu çözdü: HTTP/2'nin tek-paket (single-packet) saldırısı, çok sayıda isteğin son baytlarını tek TCP paketinde aynı anda sunucuya ulaştırır, böylece ağ gecikmesini denklemden çıkarır ve istekler sunucuda neredeyse mükemmel hizada işlenir. HTTP/1.1'de benzer etki için son-bayt senkronizasyonu kullanılır.

Pratikte test, Burp Suite'in bu tekniği uygulayan sekmesiyle yapılır; tek tıkla 20-50 istek aynı hizada gönderilir ve sonuçta beklenenden fazla "başarılı" yanıt görülürse açık doğrulanmış olur. Araç tarafını Burp Suite rehberimizde ele aldık. Önemli nokta: yarış koşulu tespiti doğrulanabilir bir açıktır; tek bir "başarılı" yanıt yetmez, aynı tek-seferlik işlemin birden çok kez gerçekleştiğini kanıtlamak gerekir.

Doğru savunma: her zaman veri katmanında

Yarış koşulunu uygulama kodundaki bir if ile çözmeye çalışmak neredeyse her zaman başarısızdır, çünkü boşluk tam da o if ile sonraki yazma arasındadır. Doğru savunma veritabanı seviyesindedir:

  • Atomik koşullu güncelleme. Kontrol ve yazmayı tek ifadede birleştirin. Örneğin UPDATE kupon SET kullanildi = true WHERE id = ? AND kullanildi = false ve etkilenen satır sayısı 1 ise işle, 0 ise reddet. Böylece kontrol ile yazma arasında boşluk kalmaz.
  • Satır kilidi. Transaction içinde SELECT ... FOR UPDATE ile ilgili satırı kilitleyin; ikinci istek ilk işlem bitene kadar bekler.
  • Benzersizlik kısıtı. "Kullanıcı başına tek oy", "kupon başına tek kullanım" gibi kuralları veritabanı UNIQUE kısıtıyla zorlayın; ikinci ekleme hata verir.
  • Idempotency anahtarı. İstemci her kritik işleme benzersiz bir anahtar eklesin; sunucu aynı anahtarı ikinci kez görürse işlemi tekrarlamaz, ilk sonucu döner. Ödeme API'lerinin standart yöntemidir.
  • Dağıtık kilit. Birden çok sunucu/instance varsa, tek veritabanı satır kilidi yetmezse Redis tabanlı dağıtık kilit (örneğin tek anahtar + TTL) kullanın.

Hız sınırlama (rate limiting) tek başına yeterli değildir çünkü yarış penceresi tek bir hız-sınırı aralığının içinde kalabilir; yine de savunmaya katkı sağlar. API tarafındaki tasarımı API güvenliği ve OWASP API Top 10 yazımızda detaylandırdık.

Sistem seviyesinde TOCTOU: sadece web değil

TOCTOU yalnızca web uygulamalarına özgü değildir. Dosya sisteminde de aynı kalıp görülür: bir program bir dosyanın sahipliğini veya varlığını kontrol eder, sonra onu açar; arada saldırgan dosyayı bir sembolik/sabit bağla değiştirirse program yanlış dosyaya yazar. Bu, CWE-367 olarak sınıflandırılır ve ciddi örnekleri vardır. Örneğin 2026'da açıklanan ve Debian/Ubuntu apt paket yöneticisini etkileyen bir açık (CVE-2026-109518), yeniden-indirme sırasında sabit-bağ yeniden kullanımı ve bir yarış koşulu nedeniyle doğrulanmamış paket kurulumuna ve kök yetkiyle kod çalıştırmaya yol açabiliyordu. Ders aynıdır: güvenlik kararını verdiğiniz an ile o karara göre iş yaptığınız an arasındaki her boşluk, bir saldırı penceresidir.

KAOS ve DSET yaklaşımı

DSET olarak yarış koşulu açıklarını iş-mantığı testinin birinci sınıf hedefi sayarız, çünkü bunlar otomatik taramaların en sık kaçırdığı ama finansal etkisi en yüksek açıklardandır. Testlerimizde tek-paket tekniğiyle kritik tek-seferlik işlemleri (kupon, çekim, davet, stok) paralel isteklerle zorlar ve açığı yalnızca tespit etmez, aynı işlemin birden çok kez gerçekleştiğini kanıtlayarak doğrularız. Geliştirdiğimiz KAOS aracı, kritik uç noktalarda atomik-olmayan kontrol-sonra-yazma kalıplarını ve eksik idempotency/kilit işaretlerini işaretler, ama son kararı her zaman kanıtlanmış yeniden-üretime bağlar; abartılı "açık var" iddiası üretmez. Uygulamanızın kritik işlemlerini yarış koşuluna karşı test ettirmek için Ankara Hacettepe Teknokent'teki ekibimizle DSET ile iletişime geçin. İlgili okuma: zafiyet taraması ile sızma testi farkı.

Sık sorulan sorular

Race condition ile TOCTOU aynı şey mi? Race condition genel kavramdır: iki işlemin zamanlamasına bağlı, öngörülemeyen sonuç. TOCTOU ise bu kavramın en yaygın güvenlik kalıbıdır: bir koşulu kontrol etmek ile o koşula göre iş yapmak arasında durumun değişmesi. Web açıklarının büyük bölümü TOCTOU biçimindedir; dosya-sistemi yarışları da öyle. Pratikte ikisi iç içe kullanılır.

Rate limiting yarış koşulunu engeller mi? Tek başına engellemez. Hız sınırlama birim zamanda istek sayısını sınırlar, ama yarış penceresi milisaniyeler içindedir ve tek bir izinli aralığın içinde birden çok istek çakışabilir. Rate limiting savunmaya katkı sağlar ve kaba-kuvvet denemelerini zorlaştırır, ama asıl çözüm atomik işlem, satır kilidi, benzersizlik kısıtı ve idempotency anahtarıdır.

Yarış koşulu açığını otomatik tarayıcı bulur mu? Çoğunlukla bulamaz. Tarayıcı tek istek gönderip yanıta bakar; yarış koşulu ise aynı anda birden çok istek ve iş-mantığı bilgisi gerektirir. Güvenilir tespit, uygulamanın tek-seferlik işlemlerini anlayan ve tek-paket tekniğiyle paralel istek gönderen manuel bir sızma testi ister. Bu yüzden kritik finansal akışlar mutlaka elle test edilmelidir.

Kaynaklar