Aikido

Derinlik koruması olmadan özyinelemeden (recursion) neden kaçınmalısınız?

Hata Riski

Kural
Derinlik koruması olmayan özyinelemeden (recursion) kaçının.
Uygun derinlik sınırlandırması olmayan özyineleme, yığın taşmasına
(stack overflow) yol açabilir ve kötü niyetli girdiler
nedeniyle DoS zafiyetleri oluşturabilir. Zorunlu derinlik sınırları ve
uygun sınır kontrollerine sahip

Dil desteği: 45+

Giriş

Derinlik sınırı olmayan özyinelemeli (recursive) fonksiyonlar çağrı yığınını tüketerek çökmelere neden olabilir. Derin iç içe geçmiş JSON nesneleri veya döngüsel veri yapıları gibi kötü niyetli girdiler, kasıtlı olarak sınırsız özyinelemeyi tetikleyebilir. Tek bir özel istek, yığın limitlerini aşarak servisinizin çökmesine neden olabilir ve bu da istismarı son derece kolay olan bir hizmet dışı bırakma (DoS) zafiyeti yaratır.

Neden önemli?

Güvenlik etkileri (DoS saldırıları): Saldırganlar, derin özyinelemeyi tetikleyen girdiler hazırlayarak uygulamanızı çökletebilir. Derin iç içe geçmiş JSON, XML veya bağlı veri yapıları yaygın saldırı vektörleridir. Tek bir kötü niyetli istek yığını tüketerek tüm servisi tüm kullanıcılar için devre dışı bırakır.

Sistem kararlılığı: Yığın taşması (stack overflow) hataları, zarif bir düşüş (graceful degradation) olmaksızın süreci anında sonlandırır. Üretim ortamında bu durum; düşen istekler, kesintiye uğrayan işlemler ve hizmet dışı kalma anlamına gelir. Kurtarma işlemi, tüm uygulamanın yeniden başlatılmasını gerektirir.

Kaynak tükenmesi: Sınırsız özyineleme, yığın belleğini üstel olarak tüketir. Her özyinelemeli çağrı bir yığın çerçevesi (stack frame) ekler ve derin özyineleme zincirleri megabaytlarca belleği tüketebilir. Bu durum aynı sunucudaki diğer süreçleri etkiler ve bellek tükenmesi (out-of-memory) durumlarını tetikleyebilir.

Kod örnekleri

❌ Kural dışı:

function processNestedData(obj) {
    if (typeof obj !== 'object' || obj === null) {
        return obj;
    }

    const result = {};
    for (const key in obj) {
        result[key] = processNestedData(obj[key]);
    }
    return result;
}

Neden yanlış: Derinlik sınırının olmaması, saldırganların yığın limitlerini aşan ve çok derin iç içe geçmiş nesneler göndermesine olanak tanır. Şunun gibi girdiler: {a: {a: {a: {...}}}} 10.000 seviye derinliğinde iç içe geçmiş yapılar, yığın taşması nedeniyle uygulamayı çökertir. Fonksiyon, derinliği kontrol etmeden körlemesine özyineleme yapar.

✅ Kurallara uygun:

function processNestedData(obj, depth = 0, maxDepth = 100) {
    if (depth > maxDepth) {
        throw new Error('Maximum nesting depth exceeded');
    }

    if (typeof obj !== 'object' || obj === null) {
        return obj;
    }

    const result = {};
    for (const key in obj) {
        result[key] = processNestedData(obj[key], depth + 1, maxDepth);
    }
    return result;
}

Neden önemli: The maxDepth parametresi özyinelemeyi 100 seviye ile sınırlandırarak yığın taşmasını önler. Bu sınır, meşru iç içe veri yapıları için yeterince yüksek (gerçek dünyadaki verilerin çoğu nadiren 10-20 seviyeyi geçer) ve önemli yığın belleği tüketilmeden önce saldırıları durduracak kadar da düşüktür. Kötü niyetli, derinlemesine iç içe geçmiş girdiler uygulamayı çökertmek yerine bir hata fırlatır. Derinlik kontrolü işlemden önce gerçekleştirilir ve sınırlar aşıldığında hızlı bir şekilde başarısız olur.

Sonuç

Harici verileri işleyen tüm özyinelemeli fonksiyonlara derinlik parametreleri ekleyin. Beklenen veri yapısı karmaşıklığına göre makul maksimum derinlikler belirleyin. Çökme yaşamak yerine, derinlik sınırları aşldığında hatalar fırlatın veya varsayılan değerler döndürün.

Sık Sorulan Sorular

Sorularınız mı var?

Makul bir maksimum özyineleme derinliği nedir?

Veri yapılarınıza bağlıdır. JSON ayrıştırma (parsing) veya ağaç tarama (tree traversal) için 100-1000 seviye makuldür. Meşru veri yapı اینکهlerinin çoğu 10-20 seviyeyi geçmez. Alanınıza (domain) göre limitler belirleyin, ancak her zaman limitleriniz olsun. Gerçek derinlikleri görmek için üretim ortamını izleyin ve buna göre ayarlamalar yapın.

Özyinelemeyi yinelemeye (iteration) nasıl dönüştürebilirim?

Açık yığınlar (explicit stacks) veya kuyruklar (queues) kullanın. Özyinelemeli çağrıları, öğeleri bir yığına iten (push), ardından bunları çıkaran (pop) ve işleyen bir döngüyle değiştirin. Bu, bellek kullanımı ve derinliği üzerinde tam kontrol sahibi olmanızı sağlar. Ağaç tarama işlemleri için, açık veri yapılarına sahip genişlik öncelikli (breadth-first) veya derinlik öncelikli (depth-first) yineleme, yığın taşmasını önler.

Derinliği özyinelemeli çağrıların başında mı yoksa sonunda mı kontrol etmeliyim?

Başında, herhangi bir işlem yapılmadan önce. Bu, limitler aşıldığında hızlı bir şekilde başarısız olmanızı sağlar ve reddedilecek veriler üzerinde boşa hesaplama yapılmasını önler. Fonksiyon girişindeki koruma ifadeleri (guard clauses), derinlik kontrollerini açık hale getirir ve denetlenmesini kolaylaştırır.

Özyinelemeli fonksiyonlardaki döngüsel referansları (cyclic references) nasıl ele almalıyım?

Ziyaret edilen nesneleri bir Set veya WeakSet içinde takip edin. Özyinelemeye (recursion) girmeden önce, nesnenin daha önce ziyaret edilip edilmediğini kontrol edin. Eğer ziyaret edildiyse, nesneyi atlayın, bir hata fırlatın ya da bir yer tutucu (placeholder) döndürün. Bu, dairesel veri yapıları (obj.child.parent === obj) nedeniyle oluşan sonsuz özyinelemeyi engeller.

Şimdi güvenliğinizi sağlayın

Kodunuzu, bulutunuzu ve çalışma zamanınızı tek bir merkezi sistemde güvenceye alın.
Güvenlik açıklarını otomatik olarak hızla bulun ve düzeltin.

Kredi kartı gerekmez | Tarama sonuçları 32 saniyede.