Bazen en tehlikeli zafiyetler herhangi bir veri sızdırmaz.
Hassas bilgiler açığa çıkmaz.
Internal servisler ifşa edilmez.
Ama tek bir istek, tüm uygulamayı tamamen kullanılamaz hale getirebilir.
Bu yazıda, basit bir GraphQL özelliğinin nasıl tam kapsamlı bir application-level Denial of Service saldırısına dönüştüğünü anlatıyorum.

Hedef uygulamada kullanıcıların tek bir shipment üzerinden batch oluşturabildiği bir özellik vardı.
Backend tarafında bu işlem bir GraphQL mutation ile yapılıyordu.
Basitleştirilmiş haliyle şöyle görünüyordu:
Normalde beklenen davranış oldukça basitti:
• Bir mutation çağrısı bir batch oluşturur
• Rate limiting istek bazlı çalışır
• Kötüye kullanım zor görünür
Dışarıdan bakıldığında güvenli bir tasarım gibi duruyordu.
Bu özellik sayesinde aynı mutation tek bir request içinde birden fazla kez çalıştırılabilir.
Örneğin:
Burada üç farklı alias vardır ama hepsi aynı işlemi yapar.
Ve kritik nokta şuydu:
Rate limiting sadece HTTP isteklerini sayıyordu.
Request içindeki mutation sayısını değil.
Bu da tek bir isteğin yüzlerce operasyon başlatabilmesi anlamına geliyordu.
Gerçekte kullandığım yapı buna benziyordu:
Tek bir HTTP isteği içinde:
175 ayrı batch oluşturma işlemi yer alıyordu.
Ortaya çıkan sonuçlar çok netti:
• 1 alias → yaklaşık 5.5 saniye
• 15 alias → yaklaşık 13.5 saniye
• 78 alias → yaklaşık 58 saniye
• 175 alias → yaklaşık 117 saniye
Alias sayısı arttıkça sunucu ciddi şekilde daha fazla kaynak tüketiyordu.
Bu da her mutation’ın gerçek ve ağır bir işlem olduğunu açıkça gösteriyordu.
Tek bir ağır istek bile sistemi zorlamaya yetiyordu.
Ancak istekler paralel gönderildiğinde etki katlanarak arttı.
75 paralel HTTP isteği gönderdim.
Her biri 175 alias içeriyordu.
Toplamda:
13.125 batch işlemi aynı anda başlatılmış oldu.
Kısa süre içinde uygulama tamamen yanıt veremez hale geldi.
Tarayıcıdan erişmeye çalıştığımda:
Cloudflare 524 timeout hatalarıyla karşılaştım.
Farklı ağlardan ve cihazlardan denediğimde de sonuç değişmedi.
Tüm platform genelinde tam bir servis kesintisi yaşanıyordu.

• Alias sayısına herhangi bir sınır yoktu
• Mutation maliyeti hesaplanmıyordu
• Rate limiting yalnızca istek seviyesindeydi
• Tüm işlemler senkron şekilde yürütülüyordu
Sonuç olarak:
Bir istek yüzlerce ağır işlem başlatıyor
Paralel istekler thread pool’u dolduruyor
Uygulama tamamen kilitleniyordu
Bu bir network saldırısı değildi.
Tamamen uygulama mantığından kaynaklanan bir DoS’tu.

Customer problemi temiz ve etkili bir şekilde çözdü.
Artık her alias çağrısı ayrı bir operasyon olarak sayılıyor.
Yani:
• Rate limiting alias başına uygulanıyor
• Tek request içinde yüzlerce mutation çalıştırmak mümkün değil
Bu sayede:
GraphQL’in esnekliği korunurken
Application-level DoS riski ortadan kaldırılmış oldu.
• Küçük bir performans yavaşlaması değil
• Geçici bir gecikme değil
Tüm platformun tamamen offline olmasına yol açıyordu.
Ve kolayca tekrarlanabiliyordu.
Bu vaka bana bir kez daha şunu gösterdi:
GraphQL çok güçlüdür, ancak doğru şekilde sınırlandırılmazsa tehlikeli olabilir.
Sadece istek sayısını sınırlamak yeterli değildir.
Her isteğin içinde kaç operasyon çalıştığını da kontrol etmek gerekir.
Alias sayısı, query complexity ve maliyet bazlı kontroller kritik öneme sahiptir.
Ve en önemlisi:
Application-level DoS çoğu zaman hafife alınır.
Ancak etkisi, birçok veri sızıntısından bile daha yıkıcı olabilir.
Hassas bilgiler açığa çıkmaz.
Internal servisler ifşa edilmez.
Ama tek bir istek, tüm uygulamayı tamamen kullanılamaz hale getirebilir.
Bu yazıda, basit bir GraphQL özelliğinin nasıl tam kapsamlı bir application-level Denial of Service saldırısına dönüştüğünü anlatıyorum.

Her Şey Normal Bir Batch Özelliğiyle Başladı
Hedef uygulamada kullanıcıların tek bir shipment üzerinden batch oluşturabildiği bir özellik vardı.
Backend tarafında bu işlem bir GraphQL mutation ile yapılıyordu.
Basitleştirilmiş haliyle şöyle görünüyordu:
JSON:
mutation CreateBatch {
createBatch(
warehouseId: "WAREHOUSE_ID",
presetId: "PRESET_ID"
) {
id
}
}
Normalde beklenen davranış oldukça basitti:
• Bir mutation çağrısı bir batch oluşturur
• Rate limiting istek bazlı çalışır
• Kötüye kullanım zor görünür
Dışarıdan bakıldığında güvenli bir tasarım gibi duruyordu.
Kırılma Noktası: GraphQL Alias Mekanizması
GraphQL’in güçlü ama çoğu zaman göz ardı edilen bir özelliği vardır: alias’lar.Bu özellik sayesinde aynı mutation tek bir request içinde birden fazla kez çalıştırılabilir.
Örneğin:
JSON:
mutation CreateBatch {
a1: createBatch(...) { id }
a2: createBatch(...) { id }
a3: createBatch(...) { id }
}
Burada üç farklı alias vardır ama hepsi aynı işlemi yapar.
Ve kritik nokta şuydu:
Rate limiting sadece HTTP isteklerini sayıyordu.
Request içindeki mutation sayısını değil.
Bu da tek bir isteğin yüzlerce operasyon başlatabilmesi anlamına geliyordu.
Basit Ama Yıkıcı Bir Payload
Alias sayısını artırarak payload’ı büyüttüm.Gerçekte kullandığım yapı buna benziyordu:
JSON:
mutation CreateBatch {
a1: createBatch(...) { id }
a2: createBatch(...) { id }
...
a175: createBatch(...) { id }
}
Tek bir HTTP isteği içinde:
175 ayrı batch oluşturma işlemi yer alıyordu.
Etkinin Net Şekilde Ortaya Çıkması
Alias sayısını artırdıkça sunucu yanıt sürelerini ölçtüm.Ortaya çıkan sonuçlar çok netti:
• 1 alias → yaklaşık 5.5 saniye
• 15 alias → yaklaşık 13.5 saniye
• 78 alias → yaklaşık 58 saniye
• 175 alias → yaklaşık 117 saniye
Alias sayısı arttıkça sunucu ciddi şekilde daha fazla kaynak tüketiyordu.
Bu da her mutation’ın gerçek ve ağır bir işlem olduğunu açıkça gösteriyordu.
Paralel İsteklerle Servisin Çökmesi
Tek bir ağır istek bile sistemi zorlamaya yetiyordu.
Ancak istekler paralel gönderildiğinde etki katlanarak arttı.
75 paralel HTTP isteği gönderdim.
Her biri 175 alias içeriyordu.
Toplamda:
13.125 batch işlemi aynı anda başlatılmış oldu.
Kısa süre içinde uygulama tamamen yanıt veremez hale geldi.
Tarayıcıdan erişmeye çalıştığımda:
Cloudflare 524 timeout hatalarıyla karşılaştım.
Farklı ağlardan ve cihazlardan denediğimde de sonuç değişmedi.
Tüm platform genelinde tam bir servis kesintisi yaşanıyordu.

Arkada Gerçekte Ne Oluyordu?
Sorunun kökeni oldukça basitti:• Alias sayısına herhangi bir sınır yoktu
• Mutation maliyeti hesaplanmıyordu
• Rate limiting yalnızca istek seviyesindeydi
• Tüm işlemler senkron şekilde yürütülüyordu
Sonuç olarak:
Bir istek yüzlerce ağır işlem başlatıyor
Paralel istekler thread pool’u dolduruyor
Uygulama tamamen kilitleniyordu
Bu bir network saldırısı değildi.
Tamamen uygulama mantığından kaynaklanan bir DoS’tu.

Nasıl Düzeltildi?
Customer problemi temiz ve etkili bir şekilde çözdü.
Artık her alias çağrısı ayrı bir operasyon olarak sayılıyor.
Yani:
• Rate limiting alias başına uygulanıyor
• Tek request içinde yüzlerce mutation çalıştırmak mümkün değil
Bu sayede:
GraphQL’in esnekliği korunurken
Application-level DoS riski ortadan kaldırılmış oldu.
Bu Bug Neden Önemli?
Bu durum:• Küçük bir performans yavaşlaması değil
• Geçici bir gecikme değil
Tüm platformun tamamen offline olmasına yol açıyordu.
Ve kolayca tekrarlanabiliyordu.
Çıkarılan Dersler
Bu vaka bana bir kez daha şunu gösterdi:
GraphQL çok güçlüdür, ancak doğru şekilde sınırlandırılmazsa tehlikeli olabilir.
Sadece istek sayısını sınırlamak yeterli değildir.
Her isteğin içinde kaç operasyon çalıştığını da kontrol etmek gerekir.
Alias sayısı, query complexity ve maliyet bazlı kontroller kritik öneme sahiptir.
Ve en önemlisi:
Application-level DoS çoğu zaman hafife alınır.
Ancak etkisi, birçok veri sızıntısından bile daha yıkıcı olabilir.
