Siber Güvenlik

Python Paketlerinde Gizli Regex Tehlikesi: 20 Kütüphane Ne Gösterdi?

Bunu yaşayan biri olarak söyleyeyim, Geçen yaz, Temmuz 2024’te İstanbul’da bir güvenlik toplantısında tam bu konuyu konuşurken biri şöyle demişti: “Regex dediğin şey basit işte.” O cümle masada iki saniye durdu. Sonra hepimiz güldük. Çünkü işin aslı şu: regex bazen kapı kilidi gibi çalışıyor — doğru anahtar gelirse açılıyor, yanlış kombinasyonda işe sistemi yavaş yavaş boğuyor, hem de öyle bir boğuyor ki küçük bir giriş metni koça bir servisi diz çöktürebiliyor. Siz hâlâ monitöre bakıp “ne oldu ya” diye düşünüyorsunuz.

Doğrusu, İşte bugün konuşacağım mesele tam bu. Python dünyasında sık kullanılan paketlerde tehlikeli regex desenleri ne kadar yaygın? Harshith Reddy Nalla’nın yaptığı çalışma bunu kurcalıyor ve açık konuşayım — çıkan tablo pek iç açıcı değil. Bu yazıyı hazırlarken 2023 sonbaharında kendi projemde yaşadığım o sınır bozucu olayı hatırladım; Ankara’daki bir müşteri panelinde tek bir log satırı yüzünden endpoint’in tepki süresi fena hâlde uzamıştı. Sonradan baktık… suçlu yine regex’ti. Klasik.

Şimdi gelelim işin can alıcı noktasına.

ReDoS Nedir ve Neden Bu Kadar Sessizdir?

ReDoS. Yani Regular Expression Denial of Service. Kulağa akademik geliyor ama pratikte gayet gündelik bir bela, inan bana.

Kullanıcıya açık alanda çalışan bir regex ifadeniz varsa ve bu ifade kötü tasarlanmışsa, saldırgan özel hazırlanmış bir girdiyle CPU’yu zıplatabiliyor — bunu söyleyince insanlar “e tamam fark edilir” diyor ama hayır, çoğu zaman fark edilmiyor. Mesela Cloudflare olayında tek bir kural değişikliği sonrası küresel trafikte %80 düşüş yaşanması boşuna değil; sistemler bazen en küçük görünen detaylarda patlıyor, inanılmaz ama gerçek.

Buradaki kritik nokta şu: problem sadece “regex var” olması değil. Problem, regex’in aynı karakteri farklı yollarla tüketebilmesi. Motor önünde birkaç yol görüyor… hepsini deniyor… sonra yine olmuyor… tekrar deniyor. Küçük metinde sorun çıkarmayan yapı, biraz uzun ve uymayan bir veri geldiğinde katlanarak büyüyor. Üstel. Kontrol edilemez.

Ben buna bazen bavul sıkıştırmaya benzetiyorum, nasıl desem — valizin içine kazakları rastgele doldurursanız kapak kapanır gibi ölür ama fermuarı zorladığınızda içeride her şey birbirine giriyor, ne çeksenin çıkmıyor. Regex de tam öyle davranıyor bazı durumlarda; düzenli görünürken içeride tam bir karmaşa yaratabiliyor (kendi tecrübem)

ReDoS’un tehlikesi şurada yatıyor: çoğu zaman saldırı “gürültülü” olmaz. Trafik normal görünür, ama arka planda CPU kıvranır.

Asıl Sorun Nerede Başlıyor?

Bi saniye — Lafı gevelemeden teknik tarafa geçeyim. Bazı regex desenleri iç içe geçen tekrarlar içeriyor. En meşhur örnek: (a+)+. Bu ifade eşleşmeyen uzun bir metne verildiğinde motor önce “belki buradan ölür” diyor, sonra başka bölünme ihtimallerini denerken süre uçup gidiyor. Küçük girişte idare eder. Biraz büyüyünce işler karışıyor. Patlıyor.

Küçük bir detay: Bunu ilk kez laboratuvar gibi kurduğum küçük bir Ubuntu VM’de test ettiğimde şaşırdım açıkçası — 10 karakterde göz kırparken biten işlem 20 karakterde neredeyse takılıp kaldı. Şaka değil. n=10 için milisaniyelerden söz ediyoruz, n=20 civarında süre dramatik biçimde şişiyor. Ve işin can sıkıcı tarafı da şu: kodunuz gayet temiz görünüyor olabilir, hatta linter’dan rahatça geçiyor. Runtime’da tokadı yersiniz.

Aşağıdaki örnek mantığı biraz daha somutlaştırıyor:

# Kötü örnek
pattern = r"(a+)+"
# Daha dikkatli yaklaşım
pattern = r"a+"

Tabii gerçek dünyada bu kadar basit çözülmüyor. E-posta doğrulama, URL ayrıştırma, log filtreleme — bunlarda desenler çok daha çetrefilli oluyor. Bir de ekipler çoğu zaman “çalışıyor mu?” sorusuna bakıp geçiyor, performans ve güvenlik kontrolü ikinci plana atılıyor. İşte hayal kırıklığı da tam burada başlıyor.

💡 Bilgi: ReDoS’u anlamanın en kolay yolu şudur: Eğer regex’ınız başarısız eşleşmede aşırı yavaşlıyorsa, sorun vardır. Başarılı eşleşme hızlı diye kendinizi kandırmayın.

sre_parse ile Statik Tarama Nasıl Yapılıyor?

Vallahi, Neyse, gelelim güzel tarafa — bu çalışmanın bence en ilginç kısmı burası.

Araştırmacılar saldırgan girdi üretip deneme yapmıyorlar; doğrudan Python’ın regex AST’sine bakıyorlar. Python regex derlenirken içerde sre_parse modülü devreye giriyor ve desenin ağacını çıkarıyor. Motorun kafasının içine bakmak gibi düşünün, tam öyle. Eğer ağaçta MAX_REPEAT düğümünün içinde yine MAX_REPEAT taşıyan bir SUBPATTERN varsa alarm çalabiliyor — bu yaklaşımın hoş yani netliği, girdiye bağımlı değilsiniz ve milyon kere test koşmanıza gerek kalmıyor çünkü risk daha kod okunurken, derleme anına yakın görülüyor (şaşırtıcı ama gerçek) Claude, mitmproxy ve Spor Uygulamasının Gizli API’si yazımızda bu konuya da değinmiştik. Daha fazla bilgi için Aşırı Gürültülü Uyarıları Susturmanın Akıllı Yolu: Pratik Rehber yazımıza bakabilirsiniz. Daha fazla bilgi için Yazılım Mühendislerinde Tükenmişlik: Sessiz İşaretler yazımıza bakabilirsiniz.

Durun, bir saniye.

Kendi deneyimimde statik analiz araçlarının hep en sevdiğim tarafı bu oldu zaten. Geçen mart ayında Kadıköy’de yaptığımız küçük bir code review oturumunda ekipten biri “önce üretimde görelim” demişti; ben tam tersini savundum (şaşırtıcı ama gerçek). Çünkü bazı hatalar üretime çıkınca değil, daha erken yakalanınca değerli oluyor. Maliyeti düşüyor, stresi düşüyor, her şey düşüyor. Yapay Zekâ Yığını: Kendi Akıllı Uygulamanı Kur yazımızda da bu konuya değinmiştik. Manticore Search’ü Grafana ile Tek Komutla İzle yazımızda da bu konuya değinmiştik.

Kategori Anlamı Neden önemli?
Nested quantification Tekrarın içinde tekrar var Patlama etkisi yaratabilir
nulls within quantifications Tekerleme gibi boş eşleşmeler oluşabilir Motor sonsuz dallanmaya yaklaşabilir
overlapping alternatives Aynı şeyi yapan alternatifler üst üste biner Eşleşme denemeleri artar
beklenmedik kategori Araştırmanın bulduğu ekstra risk tipi Daha önce fark edilmeyebilir

Peki Hangi Paketlerde Risk Var?

Tuhaf ama, Araştırma ekibi Python ekosistemindeki popüler paketleri taramış: requests, flask, django, fastapi, sqlalchemy, pydantic, pytest, numpy… liste uzayıp gidiyor. Dürüst olayım — bazı isimleri görünce insan otomatik olarak “bunlarda sorun çıkmaz herhalde” diye düşünüyor. Büyük işim, büyük güven. Ama büyük paket demek kusursuz paket demek değil. Hiç değil aslında.

Bence asıl ders şu: açık kaynakta kullanılan yardımcı fonksiyonlar bile saldırı yüzeyi oluşturabiliyor. Bir proje kendi içinde mükemmel güvenlik duvarlarına sahip olabilir. Üçüncü parti paketteki ufak bir pattern tüm resmî bozabiliyor — özellikle input validation tarafında bu çok daha kritik. Kurumsal tarafta bunu daha sert hissediyorsunuz. Etki alanı geniş; küçük startup’ta tek servis yavaşlarken enterprise ortamda beş ayrı ürün aynı anda tökezleyebiliyor.

Küçük ekiplerde neden kaçırılıyor?

Hız baskısı. Hepsi bu kadar. “Şu form çalışsın yeter” deniyor ve regex ince ayarı erteleniyor. Sonra trafik biraz artınca gecikmeler başlıyor. Kimse ilk bakışta sebebi bulamıyor — çünkü hata stack trace bırakmıyor, sessiz ilerliyor, sinsi bir şekilde.

Büyük organizasyonlarda neden daha da kritik?

Büyük organizasyonlarda olay sadece performans meselesi değil. SLA var, müşteri güveni var, hatta bazen regülasyon baskısı var. Benim gördüğüm kadarıyla kurumsal yapılarda en pahalı şey CPU maliyeti bile olmuyor — asıl pahalı olan yanlış alarm zinciri ve incident yönetimi sırasında kaybedilen saatler oluyor (inanın bana). E peki, sonuç ne oldu? Gerçekten.

  • Kullanıcıdan gelen veri doğrudan regex’e giriyorsa dikkat edin.
  • Nesting yani iç içe tekrarlar özellikle risklidir. — ciddi fark yaratıyor
  • Eşleşmeyen uzun girdilerle mutlaka test yapın.
  • Paket güncellemelerinde security notlarını okumayı alışkanlık hâline getirin.
  • Mümkünse statik analiz + dinamik test ikilisini birlikte kullanın.

Bana Göre En Pratik Savunma Katmanları

Açık konuşayım: herkesin hemen kompleks araçlara koşmasına gerek yok. İlk savunma katmanı sade tasarım olmalı. Regex’i mümkün olduğunca kısa tutun, alternatifleri azaltın, gereksiz yıldızları üst üste bindirmeyin. Çoğu ekipte sorun teknik değil zaten — disiplin sorunu, biraz bakım kültürü yetiyor aslında.

Şöyle toparlayayım: eğer kullanıcı girdisi üzerinde çalışıyorsanız timeout koymak şarttır. Üstelik sadece uygulama seviyesinde değil, proxy (belki yanilıyorum ama) veya WAF tarafında da sınır koymak gayet mantıklı. Ben geçen yıl Eylül ayında İzmir’de yapılan bir hackathon’da bunu birebir gördüm; timeout olmayan bir demo sunumu yarıda kesilmişti çünkü masum görünen bir pattern yarışmayı kilitlemişti. Salonda sessizlik. Utanç. Sonra uzun bir “hata nerede” tartışması.

Neleri kontrol etmeli?

  • Zaman aşımı sınırı belirleyin.
  • Düzenli expression’ları kod incelemesinde ayrıca değerlendirin.
  • Kritik endpoint’lerde yük testi yapın.
  • Saldırı simülasyonu yerine güvenlik odaklı regresyon testleri ekleyin.
  • Kullanılan paketlerin sürümlerini takıp edin.

Sıkça Sorulan Sorular

ReDoS (Regular Expression Denial of Service) tam olarak ne yapar?

ReDoS, kötü yazılmış regex desenlerinin bazı girişlerde aşırı zaman harcamasına yol açarak hizmeti yavaşlatması ya da durdurmasıdır. Saldırgan “gürültülü” bir şey yapmadan, normal görünen ama özel hazırlanmış metinlerle CPU’yu gereksiz yere meşgul eder. Bu yüzden çoğu sistemde hemen fark edilmez.

Regex’i “güvenlik sorunu” yapan şey sadece regex kullanmak mı?

Hayır, sorun genelde regex’in tasarımıdır; özellikle iç içe tekrarlar ve geri izleme (backtracking) maliyetli kalıplar risk oluşturur. Motorun aynı metin için birden fazla yolu deneyip dengeyi kaybetmesi kritik. Benim de bir projede tek bir log satırı yüzünden endpoint tepki süresinin ciddi uzadığını görmüştüm; suçlunun regex olduğunu sonradan tespit ettik.

ReDoS belirtileri neler, nasıl anlaşılır?

En sık gördüğünüz belirti, belirli isteklerde CPU kullanımının aniden yükselmesi ve yanit sürelerinin “aşırı” uzamasıdır. Trafik normal görünse bile gecikmeler artıyorsa regex kaynaklı durum olabilir. Uygulama tarafında regex eşleşme sürelerini ölçmek ve şüpheli desenleri izole test etmek genelde hızlı ipucu verir.

“(a+)+” gibi desenler neden bu kadar tehlikeli?

Çünkü bu tür kalıplar, eşleşmeyen ya da beklenmeyen uzun girdilerde motorun çok fazla kombinasyonu denemesine neden olur. Motor “olmazsa şöyle denerim” diye geri izlemeye girer ve süre üstel şekilde büyüyebilir. Kısa metinlerde sorun çıkarmayan desenlerin, veri biraz büyüyünce patlaması bu yüzden çok yaygın.

ReDoS riskini azaltmak için pratikte ne yapabilirim?

Önce riskli desenleri tarayıp (özellikle iç içe tekrarlar) mümkünse daha deterministik regex’lere geçmek iyi bir başlangıç. Ayrıca eşleşme için zaman limiti, giriş boyutu kısıtı ve regex motoru/ayarlarının doğru kullanımı da etkili olur. Ben genelde önce en çok çağrılan regex’leri profillendirip sonra tek tek düzeltmeye gidiyorum; böylece “nerede zaman gidiyor?” sorusunu netleştiriyoruz.

Kaynaklar ve İleri Okuma

.NET Regex backtracking ve performans konuları

Regular Expression Language (Quick Reference)

Cloudflare: Regex DoS (ReDoS) nedir?

RE2: Performans ve ReDoS’a yaklaşım

Aşkın KILIÇ

20+ yıl deneyimli Azure Solutions Architect. Microsoft sertifikalı bulut mimari ve DevOps danışmanı. Azure, yapay zekâ ve bulut teknolojileri üzerine Türkçe teknik içerikler üretiyor.

AZ-305AZ-104AZ-500AZ-400DP-203AI-102

Bu içerik işinize yaradı mı?

Benzer içerikleri kaçırmamak için beni sosyal medyada takip edin.

← Onceki Yazi
Claude, mitmproxy ve Spor Uygulamasının Gizli API’si
Sonraki Yazi →
Claude Code ile Çoklu Repo Yönetimi: Context Kaybetme

Yorum Yaz

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

İçindekiler
← Claude, mitmproxy ve Spor Uygu...
Claude Code ile Çoklu Repo Yön... →