Aikido

ReDoS saldırılarını önlemek için yavaş düzenli ifadelere (regular expressions) karşı neden önlem almalısınız?

Okunabilirlik

Kural:
Yavaş düzenli ifadelere karşı koruma
sağlayın. İç içe niceleyicilere veya 
belirsiz kalıplara sahip düzenli ifadeler 
felaket düzeyinde geri izlemeye (backtracking)
ve performans sorunlarına yol açabilir.
Desteklenen diller: 45+

Giriş

Düzenli ifadeler (regex), doğru girdiyle uygulamanızı saniyelerce veya dakikalarca dondurabilir. Felaket düzeyindeki geri izleme (backtracking), regex motorları bir kalıbı eşleştirmeye çalışırken üstel olarak artan yolları keşfettiğinde meydana gelir. Şunun gibi bir regex: (a+)+b geçerli bir girdiyi mikrosaniyeler içinde eşleştirebilir, ancak takip eden bir b harfi olmaksızın bir dizi a harfini reddetmek saatler sürebilir. Saldırganlar bunu Düzenli İfade Hizmet Dışı Bırakma (ReDoS) saldırıları yoluyla istismar eder; regex motorunuzun istek zaman aşımı gerçekleşene veya işlem çökene kadar %100 CPU tüketmesine neden olan özel hazırlanmış girdiler gönderirler.

Neden önemli?

Güvenlik etkileri (ReDoS saldırıları): Bir saldırgan, özel olarak hazırlanmış girdi içeren tek bir istekle uygulamanızı felç edebilir. E-posta doğrulama ve URL ayrıştırma kalıpları yaygın hedeflerdir. Bant genişliği gerektiren geleneksel DoS saldırılarının aksine ReDoS yalnızca çok küçük yükler (payload) gerektirir.

Performans düşüşü: Normal kullanıcı girdisi felaket düzeyinde geri izlemeyi tetikleyebilir ve yanıt sürelerinin milisaniyelerden saniyelere fırlamasına neden olabilir. Bu durum, yalnızca belirli girdi kalıplarında ortaya çıktığı için hata ayıklaması zor olan öngörülemeyen bir gecikme yaratır.

Prodüksiyon olayları: Zafiyet içeren regex, Node.js'teki olay döngüsünü (event loop) bloke eder veya iş parçacığı havuzu (thread pool) kaynaklarını tüketir. İstekler biriktikçe bellek artar ve sistem yanıt vermez hale gelir. Mikro hizmetlerde, zafiyet içeren tek bir regex hataları bağımlı servislere zincirleme olarak aktarır.

Tespitteki zorluklar: Kısa girdilerle yapılan testlerde sorunsuz çalışan kalıplar, daha uzun girdilerle üstel olarak yavaşlar. Zafiyet genellikle prodüksiyona geçene kadar fark edilmez ve aktif bir olay sırasında acil bir dağıtım (deployment) yapılmasını gerektirir.

Kod örnekleri

❌ Kural dışı:

function validateEmail(email) {
    const regex = /^([a-zA-Z0-9_\-\.]+)+@([a-zA-Z0-9_\-\.]+)+\.([a-zA-Z]{2,5})$/;
    return regex.test(email);
}

function extractURLs(text) {
    const regex = /(https?:\/\/)?([\w\-])+\.(\w+)+([\w\-\.,@?^=%&:/~\+#]*)+/g;
    return text.match(regex);
}

Güvensiz olma nedeni: İç içe geçmiş niceleyiciler ([a-zA-Z0-9_\\-\\.]+)+ üstel geri izlemeye (backtracking) yol açar. Buna benzer bir e-posta adresinde aaaaaaaaaaaaaaaaaaaaaaaaa!, regex motoru başarısız olmadan önce sayısız kombinasyon dener. URL regex'i, problemi katlayan birden fazla iç içe geçmiş niceleyici içerir; bu da beklenen yapıya sahip olmayan uzun geçerli karakter dizileri gibi girdilerle burayı istismar etmeyi son derece kolaylaştırır.

✅ Kurallara uygun:

function validateEmail(email) {
    const regex = /^[a-zA-Z0-9_\-\.]+@[a-zA-Z0-9_\-\.]+\.[a-zA-Z]{2,5}$/;
    return regex.test(email);
}

function extractURLs(text) {
    const regex = /https?:\/\/[\w\-]+\.[\w\-]+(?:[\w\-\.,@?^=%&:/~\+#]*)?/g;
    return text.match(regex);
}

Güvenli olma nedeni: İç içe geçmiş niceleyicilerin kaldırılması felaket niteliğindeki geri izlemeyi (catastrophic backtracking) ortadan kaldırır. [a-zA-Z0-9_\-\.]+ gibi tekli niceleyiciler doğrusal zamanda yürütülür. URL deseni, iç içe tekrarlar yerine isteğe bağlı soneke (?:...)? sahip yakalamayan gruplar (non-capturing groups) kullanarak girdi uzunluğundan veya içeriğinden bağımsız olarak öngörülebilir bir performans sağlar.

Sonuç

Düzenli ifade (regex) performansı yalnızca bir optimizasyon meselesi değil, bir güvenlik konusudur. Tüm regex kalıplarını iç içe geçmiş niceleyiciler, tekrar gruplarındaki çakışan karakter sınıfları ve belirsiz alternatifler açısından gözden geçirin. Dağıtımdan önce felaket niteliğindeki geri izlemeleri tespit etmek için regex kalıplarını patolojik girdilerle (geçersiz bitişlerle takip edilen uzun geçerli karakter dizileri) test edin. Mümkün olduğunda, karmaşık regex'leri öngörülebilir performans özelliklerine sahip dize ayrıştırma (string parsing) fonksiyonlarıyla değiştirin.

Sık Sorulan Sorular

Sorularınız mı var?

Hangi kalıplar felaket niteliğindeki geri izlemeye neden olur?

Yaygın tetikleyiciler arasında (a+)+, (a*)* veya (a+)*b gibi iç içe geçmiş niceleyiciler yer alır. (a|a)* veya (a|ab)* gibi çakışan kalıplarla yapılan alternatifler ve (a?)+ gibi isteğe bağlı bileşenlere sahip tekrarlar da buna dahildir. Regex motorunun aynı alt dizeyi birden fazla şekilde eşleyebildiği her kalıp, üstel bir arama uzayı yaratır. Kendi içinde nicelenmiş grupların içindeki niceleyicilere (+, *, {n,m}) dikkat edin.

Regex'imin ReDoS'a karşı savunmasız olup olmadığını nasıl test edebilirim?

Yürütme adımlarını gösteren ve felaket niteliğindeki geri izleme konusunda uyarılarda bulunan regex101.com gibi çevrimiçi araçları kullanın. Uzun geçerli karakter dizileri ve ardından geri izlemeyi zorlayan karakterlerden oluşan test girdileri oluşturun. /^(a+)+b$/ kalıbı için "aaaaaaaaaaaaaaa!" (30+ tane 'a', 'b' yok) ile test edin. Eğer yürütme milisaniyelerden uzun sürüyorsa, regex savunmasızdır. Derinlemesine savunma (defense in depth) için prodüksiyon ortamındaki regex işlemlerine zaman aşımı (timeout) ekleyin.

Felaket niteliğindeki geri izleme ile doğrusal geri izleme arasındaki fark nedir?

Doğrusal geri izleme, regex alternatifleri sırayla denediğinde ancak önceki seçimleri yeniden değerlendirmediğinde gerçekleşir. Yapılan iş, girdi boyutuyla doğrusal olarak büyür. Felaket niteliğindeki geri izleme ise iç içe geçmiş niceleyiciler motoru üstel sayıda kombinasyonu denemeye zorladığında ortaya çıkar. n uzunluğundaki bir girdi için yürütme süresi O(2^n) veya daha kötü olabilir. Aradaki fark, mütevazı girdi boyutları için milisaniyeler ile dakikalar arasındaki farktır.

Öngörülü arama (lookahead) ve geriye dönük arama (lookbehind) ifadelerini güvenli bir şekilde kullanabilir miyim?

Lookaheads (?=...) and lookbehinds (?<=...) themselves don't cause catastrophic backtracking, but they can hide vulnerable patterns. A lookahead containing (a+)+ is still vulnerable. Use lookarounds for their intended purpose (assertions without consuming characters), not as a workaround for complex matching. Keep the patterns inside lookarounds simple and test them thoroughly.

Felaket niteliğindeki geri izlemeyi önleyen regex motorları var mı?

Google tarafından kullanılan RE2, geri izlemeyi tamamen yasaklayarak doğrusal zamanlı yürütmeyi garanti eder. Tüm özellikleri desteklemez (geri başvurular, lookaround'lar) ancak ReDoS'u tamamen engeller. Kritik güvenlik kontrolleri için RE2 bağlamalarını (bindings) veya benzer motorları kullanmayı düşünün. JavaScript için yerleşik bir alternatif yoktur, bu nedenle kalıp tasarımı ve zaman aşımı mekanizmaları birincil savunmanızdır.

Tüm regex işlemlerine zaman aşımı eklemeli miyim?

Güvenilmeyen girdiler (kullanıcı tarafından sağlanan veriler, harici API yanıtları) için evet. Beklenen karmaşıklığa bağlı olarak 100-500ms gibi makul zaman aşımları ayarlayın. Node.js'te regex.test()'e doğrudan zaman aşımı ekleyemezsiniz, ancak önce girdi uzunluğunu doğrulayabilir veya regex'i zaman aşımı olan bir işçi iş parçacığında (worker thread) çalıştırabilirsiniz. Regex eşleştirmesi yapmadan önce makul uzunluk sınırlarını aşan girdileri reddedin.

Mevcut savunmasız bir regex kalıbını nasıl düzeltirim?

İlk olarak, gerçekten regex'e ihtiyacınız olup olmadığını belirleyin. Birçok doğrulama görevi includes(), startsWith() veya split() gibi dize (string) metotlarıyla daha kolay çözülür. Eğer regex gerekliyse, kalıbı düzleştirerek iç içe geçmiş niceleyicileri ortadan kaldırın. Regex motorunuz destekliyorsa (a+)+ yerine a+ kullanın, atomik gruplar veya sahiplenici niceleyiciler (possessive quantifiers) tercih edin. Karmaşık kalıplar için girdiyi daha basit regex veya dize işlemleriyle çoklu geçişlerde (multi-pass) ayrıştırmayı düşünün.

Ş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.