Harika, bir önceki modül olan kriptografi konusunu tamamladık. Orada, sağlam temellere dayanan standartların ve protokollerin ne kadar önemli olduğunu gördük. Şimdi ise rotamızı, dillerin kendi iç dinamiklerinden ve uygulama zamanı (runtime) ortamlarının özelliklerinden kaynaklanan zafiyetlere çeviriyoruz. Bu dersle birlikte Dinamik Dillerde Güvenlik Tuzakları: Python & JavaScript modülüne başlıyoruz.
Giriş dersimizde, tek bir satır kodun bütün bir sunucuyu nasıl durma noktasına getirebileceğini inceleyeceğiz. Konumuz, özellikle Node.js gibi tek iş parçacıklı, olay döngüsü (event loop) tabanlı ortamlarda yıkıcı etkilere sahip olabilen Düzenli İfade Tabanlı Hizmet Reddi veya kısaca ReDoS (Regular Expression Denial of Service). Bu dersin sonunda, JavaScript (Node.js) kodunda ReDoS zafiyetlerini tespit etme yetkinliğini kazanacaksınız. Bu, yalnızca bir zafiyet bulmakla kalmayıp, uygulamanın mimari zayıflığıyla (event loop blokajı) birleştiğinde nasıl kritik bir risk oluşturduğunu anlama yeteneğinizi geliştirecektir.
ReDoS: Felakete Giden Geri İzleme (Catastrophic Backtracking)
Düzenli ifadeler (regex), metin işleme için son derece güçlü araçlardır. Ancak bu gücün bir bedeli vardır. Regex motorları, bir eşleşme bulmak için tasarlanmış durum makineleridir. Bazı "kötü niyetli" veya "verimsiz" yazılmış desenler, regex motorunu, bir eşleşme bulmak (veya bulamamak) için üstel sayıda olasılığı denemeye zorlayabilir. Bu duruma felaket geri izleme (catastrophic backtracking) denir.
Matematik altyapınızdan yola çıkarak bunu bir kombinatoryal patlama olarak düşünebilirsiniz. Motor, bir eşleşme bulamadığında, daha önce denediği yollardan geri döner ve diğer tüm olası kombinasyonları dener. Eğer desen, bir dizenin bölümlenmesi için çok sayıda yol sunuyorsa, denenecek yolların sayısı dizenin uzunluğuyla birlikte katlanarak artar.
Bu soruna yol açan temel "kötü niyetli regex" (evil regex) desenleri genellikle şu üç kategoriden birine girer:
- İç İçe Niceleyiciler (Nested Quantifiers): Bir tekrar (
*veya+) içeren bir grubun kendisinin de tekrarlanması. Örnek:(a+)+ - Tekrarlanan Alternatifler (Alternation with Repetition): Birbirini kapsayan veya ortak başlangıçlara sahip seçenekler içeren bir alternatif grubunun (
|) tekrarlanması. Örnek:(a|aa)+ - Çakışan Gruplar (Overlapping Groups): Bir grubun içeriği, aynı grubun bir sonraki tekrarıyla çakışıyorsa. Örnek:
(a.*a)
Bu desenlerin en ünlüsü (a+)+ yapısıdır. Motor, aaaaaaaaaaaa...b gibi bir dizeyle karşılaştığında, a karakterlerinden oluşan diziyi içteki + ve dıştaki + arasında bölüştürmenin tüm olası yollarını dener. Dize eşleşmediği için (sondaki b yüzünden), bu denemelerin hepsi başarısız olur, ancak motor bunu anlamadan önce milyarlarca kombinasyonu test etmek zorunda kalabilir.
Aşağıdaki görsel, bu üstel artışı somut bir şekilde göstermektedir. (x+)+y gibi kötü niyetli bir regex'in, girdi dizesinin uzunluğu arttıkça işlem süresini nasıl patlattığını görebilirsiniz.

Node.js'te ReDoS: Tek İş Parçacığının Laneti
ReDoS teorik olarak herhangi bir dilde mevcut olsa da, Node.js'te özellikle tehlikelidir. Node.js'in performansının sırrı, engellemeyen G/Ç (non-blocking I/O) operasyonlarını yöneten tek bir olay döngüsüne (event loop) dayanmasıdır. Bu döngü, gelen istekleri alır, G/Ç gerektiren işleri (dosya okuma, veritabanı sorgusu vb.) arka plan iş parçacıklarına devreder ve kendisi diğer istekleri işlemeye devam eder.
Ancak, CPU-yoğun bir işlem olay döngüsünü meşgul ederse, başka hiçbir işlem yapılamaz. Regex eşleştirmesi tam da böyle bir CPU-yoğun işlemdir. Kötü niyetli bir girdiyle tetiklenen bir ReDoS zafiyeti, olay döngüsünü saniyelerce, hatta dakikalarca kilitleyebilir. Bu süre boyunca sunucu yeni gelen hiçbir isteğe yanıt veremez, bu da tam teşekküllü bir hizmet reddi saldırısı anlamına gelir.
Kodda ReDoS Desenlerini Tespit Etme
Bir kod denetçisi olarak göreviniz, bu tehlikeli desenleri proaktif olarak bulmaktır. Genellikle aşağıdaki gibi yerlerde bulunurlar:
- Girdi doğrulama mantığı (e-posta, URL, karmaşık parola kuralları)
- Web framework'lerinin yönlendirme (routing) mekanizmaları
- HTML sanitizasyonu veya metin ayrıştırma (parsing) işlemleri
- WAF kuralları veya güvenlik filtreleri
İşte tipik bir ReDoS zafiyeti içeren JavaScript kodu örneği:
// E-posta doğrulama için kullanılan potansiyel olarak zafiyetli bir regex
// Sorunlu kısım: (([a-z\d\.-]+)\.)+
const emailRegex = /^([a-z\d\.-]+)@(([a-z\d\.-]+)\.)+([a-z]{2,4})$/i;
function validateEmail(email) {
// Kısa ve geçerli e-postalar için sorun yok
console.log(`Testing: ${email}`);
const startTime = Date.now();
emailRegex.test(email);
const endTime = Date.now();
console.log(`Time taken: ${endTime - startTime}ms`);
}
// Geçerli e-posta
validateEmail("test@example.com");
// Kötü niyetli girdi (uzun bir alt alan adı ve eşleşmeyen bir son karakter)
validateEmail("test@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com!");
Yukarıdaki (([a-z\d\.-]+)\.)+ yapısı, klasik bir iç içe niceleyici desenidir. . karakteri hem iç grup ([a-z\d\.-]+) hem de dış grup tarafından (\.) işlenebildiği için bir çakışma ve belirsizlik yaratır. Regex motoru, sonundaki ! karakteri nedeniyle eşleşmenin başarısız olacağını anlamadan önce, a'lardan oluşan dizeyi bu iki grup arasında bölüştürmenin tüm yollarını deneyecektir. Bu da işlem süresini felç eder.
Çözüm Yolları: Regex'i Evcilleştirmek
ReDoS'u önlemek, regex kullanmaktan vazgeçmek anlamına gelmez. Bunun yerine, onu güvenli bir şekilde kullanmayı öğrenmek gerekir.
1. Deseni Yeniden Yazmak ve Basitleştirmek
En temel çözüm, zafiyetli deseni ortadan kaldırmaktır. Çoğu durumda, iç içe niceleyiciler gereksizdir ve daha basit, lineer zaman karmaşıklığına sahip bir desenle değiştirilebilir. Örneğin, (a+)+ ifadesi neredeyse her zaman a+ ile aynı şeyi eşleştirir, ancak ikincisi tamamen güvenlidir.
2. Atomik Gruplar (Atomic Grouping)
Bu, ReDoS'u engellemek için çok güçlü bir tekniktir. Atomik grup (?>...) ile tanımlanan bir desen, regex motoruna şu talimatı verir: "Bu grubu bir kez eşleştirdiğinde, bu karara sadık kal ve asla bu grubun içine geri dönüp başka bir olasılığı deneme (backtrack yapma)." Motor, bulduğu ilk eşleşmeyi kabul eder ve ilerler.
Aşağıdaki görsel, kötü niyetli bir desenin yarattığı kaotik arama ağacı ile atomik gruplama kullanılarak elde edilen lineer ve verimli yolu karşılaştırmaktadır.

Önemli Not: Standart JavaScript regex motoru, atomik grupları (?>...) veya bunun kısa yolu olan sahipleyici niceleyicileri (*+, ++) desteklemez. Ancak bu kavram, PCRE (Perl Compatible Regular Expressions) gibi birçok diğer regex motorunda mevcuttur ve farklı dillerde veya güvenlik araçlarında (örneğin WAF'lar) kod incelerken bu yapıları bilmek son derece önemlidir.
3. Girdi Uzunluğunu Sınırlamak
En pragmatik ve kurşun geçirmez yöntemlerden biri budur. Regex ne kadar karmaşık olursa olsun, ReDoS saldırısı ancak yeterince uzun bir girdiyle tetiklenebilir. Regex'i çalıştırmadan önce girdinin makul bir uzunlukta olduğunu (örneğin, bir e-posta adresi için 256 karakter) kontrol etmek, saldırıyı en başından engeller. Bu, bir "savunma derinliği" katmanı ekler.
4. Otomatik Araçlar ve Kütüphaneler Kullanmak
Kod denetimi sürecinde, potansiyel olarak zafiyetli regex'leri tespit edebilen araçlardan faydalanabilirsiniz. Express.js için hazırlanan güvenlik rehberi de bu konuya değinmektedir.
En İyi Canlı Ortam Pratikleri: Güvenlik - Express
Bu kaynak, Node.js uygulamalarını güvence altına almak için en iyi pratikleri özetliyor ve ReDoS konusuna doğrudan bir referans içeriyor.
Lütfen "Ek hususlar" başlığı altındaki son maddeye odaklanın. Orada, ReDoS saldırılarına karşı safe-regex gibi paketlerin kullanılması önerilmektedir. Bu tür araçlar, CI/CD işlem hattınıza entegre edilebilir veya manuel kod denetimi sırasında hızlı bir ilk kontrol olarak kullanılabilir. Bu, DevSecOps alanındaki deneyiminizle de örtüşen pratik bir yaklaşımdır.
Özet ve Sonraki Adımlar
Bu derste, basit gibi görünen bir regex'in nasıl bir hizmet reddi zafiyetine dönüşebileceğini derinlemesine inceledik.
Anahtar Çıkarımlar:
- ReDoS, regex motorunun "felaket geri izleme" yapmasına neden olan kötü niyetli desenlerden (örneğin iç içe niceleyiciler) kaynaklanır.
- Node.js'in tek iş parçacıklı olay döngüsü mimarisi, ReDoS'u özellikle tehlikeli hale getirir çünkü tüm uygulamayı bloke edebilir.
- Kod denetiminde, özellikle girdi doğrulama, yönlendirme ve ayrıştırma mantığındaki regex desenleri dikkatle incelenmelidir.
- Çözüm için deseni basitleştirmek, (desteklenen ortamlarda) atomik gruplar kullanmak ve en önemlisi girdi uzunluğunu doğrulamak gibi stratejiler uygulanmalıdır.
ReDoS, olay döngüsünü bloke etmenin yollarından sadece biridir. Bir sonraki dersimizde, Node.js'teki bu temel mimari özelliğini istismar etmenin diğer yollarını inceleyeceğiz. Konumuz, Node.js'te CPU-yoğun operasyonlar veya senkron I/O nedeniyle oluşan olay döngüsü blokajı (event loop blocking) desenlerini belirlemek olacak.