NoSQL Injection: MongoDB Operatör Enjeksiyonu ve Korunma
NoSQL veritabanı SQL kullanmaz ama enjeksiyondan bağışık değildir. Kullanıcı girdisi sorgu nesnesine karışınca saldırgan $ne, $gt, $regex gibi operatörlerle kimlik doğrulamayı atlayabilir veya veri çekebilir. NoSQL injection nasıl çalışır, operatör ve JavaScript enjeksiyonu örnekleri ve girdi türü doğrulama ile korunma.
Hızlı cevap: NoSQL injection, MongoDB gibi SQL kullanmayan veritabanlarında, kullanıcı girdisinin sorgu nesnesine güvenilmeden karışması sonucu sorgunun mantığının değiştirilmesidir. Klasik örnek operatör enjeksiyonudur: giriş formunda parola yerine
{"$ne": null}(null'a eşit değil) göndermek, "parolası herhangi bir şey olan kullanıcı" anlamına gelir ve kimlik doğrulamayı atlatabilir.$gt,$regex,$wheregibi operatörlerle veri sızdırma ve koşul atlama da mümkündür. SQL injection'dan farkı tırnak kaçışı değil, girdinin sorgu nesnesindeki rolüdür. Çözüm girdiyi beklenen türe zorlamak (string gelmeli, nesne değil), girdiyi sorgu yapısına değil yalnızca değer olarak kullanmak,$whereve sunucu-taraflı JavaScript'i kapatmaktır.
"NoSQL kullanıyoruz, SQL injection bize işlemez" yanılgısı yaygındır ve tehlikelidir. Enjeksiyon SQL'e özgü değildir; kullanıcı girdisinin bir sorgunun yapısını değiştirebildiği her yerde vardır. MongoDB ve benzeri belge veritabanlarında bu, tırnak işaretiyle değil, sorgu operatörleriyle olur. Bu rehber NoSQL injection'ın nasıl çalıştığını, en sık görülen kimlik-doğrulama atlatma ve veri sızdırma örneklerini, SQL injection'dan farkını ve doğru savunmayı anlatır.
SQL injection'dan farkı: tırnak değil, operatör
SQL injection'da saldırgan genelde tırnak işareti ve SQL anahtar kelimeleriyle sorguyu kırar. NoSQL'de ise sorgular çoğu zaman JSON benzeri nesnelerdir ve saldırı, girdinin bir değer yerine bir operatör nesnesi olarak yorumlanmasıyla gerçekleşir.
Klasik örnek: giriş sorgusu { kullanici: girilen_ad, parola: girilen_parola } biçiminde kuruluyor. Uygulama istek gövdesini doğrudan bu nesneye koyuyorsa, saldırgan parola alanına düz metin yerine {"$ne": null} gönderebilir. Sorgu artık "parolası null'a eşit olmayan kullanıcı" anlamına gelir, yani parolası dolu olan ilk kullanıcıyla eşleşir ve kimlik doğrulama atlanır. Benzer biçimde {"$gt": ""} (boştan büyük) de tüm kayıtlarla eşleşir.
Buradaki kök neden, istek gövdesindeki bir alanın string beklenirken nesne olarak kabul edilmesidir. JSON API'lerde bu özellikle kolaydır çünkü istemci "parola": "123" yerine "parola": {"$ne": null} göndermekte serbesttir ve sunucu türü kontrol etmezse bunu olduğu gibi sorguya koyar.
En sık görülen saldırı biçimleri
| Teknik | Örnek | Etki |
|---|---|---|
$ne / $gt ile kimlik atlatma |
{"$ne": null} parola alanında |
Parola bilmeden giriş |
$regex ile veri çıkarımı |
{"$regex": "^a"} ile alan harf harf tahmin |
Gizli değeri kör sızdırma |
$where / JavaScript enjeksiyonu |
"this.gizli == x" benzeri ifade |
Sunucuda mantık çalıştırma, DoS |
| Operatör ile filtre atlatma | Yetki/sahiplik filtresini $ne ile bozma |
Başka kullanıcının verisi |
| Dizi/nesne ile mantık bozma | Beklenen string yerine dizi | Beklenmeyen sorgu davranışı |
$regex tabanlı saldırılar özellikle sinsidir: saldırgan gizli bir değeri (örneğin bir sıfırlama anahtarı) doğrudan göremese bile, karakterleri tek tek deneyerek (önce ^a, sonra ^ab ...) yanıtın değişmesinden değeri çıkarabilir. Bu, SQL'deki kör enjeksiyonun (blind injection) NoSQL karşılığıdır. $where operatörü ise sunucuda rastgele JavaScript çalıştırmaya kadar gidebildiği için en tehlikeli olanıdır ve genelde tamamen kapatılmalıdır.
Nasıl tespit edilir
Tespit, girdinin tür sınırının test edilmesiyle başlar. Pentester bir alana string yerine operatör nesnesi ({"$ne": null}) veya dizi göndererek davranışın değişip değişmediğine bakar: giriş beklenmedik biçimde başarılı oluyor, farklı bir hata dönüyor veya yanıt süresi/uzunluğu değişiyorsa açık işaret verir. $regex ile karakter-karakter çıkarım, kör durumda değerin sızdırılabildiğini kanıtlar.
Bu testler meşru isteği yakalayıp gövdeyi düzenleyerek yapılır; Burp Suite rehberimizde anlattığımız akış burada da temeldir. Enjeksiyon sınıfının genel mantığını SQL injection yazımızda ayrıntılı ele aldık; NoSQL injection aynı kök nedenin (girdi ile komut/sorgu yapısının karışması) farklı bir tezahürüdür.
Doğru savunma: tür doğrulama ve değer ayrımı
NoSQL injection'ın çözümü, girdinin sorgu yapısını değil yalnızca değeri belirlemesini garantilemektir:
- Girdi türünü zorlayın. Parola, kullanıcı adı gibi alanlar string olmalıdır. İstek gövdesinde bu alan bir nesne veya dizi olarak geldiyse reddedin. Bir doğrulama şeması (örneğin katı tip tanımlı) bunu otomatik yapar ve
{"$ne": null}gibi girdileri daha sorguya ulaşmadan eler. - Girdiyi değer olarak kullanın. Sorgu nesnesini kullanıcı girdisiyle doğrudan birleştirmeyin; alanı açıkça string'e dönüştürüp öyle kullanın, böylece operatör nesnesi anlamını yitirir.
$whereve sunucu-taraflı JavaScript'i kapatın. Bu özellikler çoğu uygulamada gereksizdir ve en tehlikeli saldırı yüzeyini oluşturur; veritabanı seviyesinde devre dışı bırakın.- En az yetki. Uygulamanın bağlandığı veritabanı kullanıcısı yalnızca ihtiyaç duyduğu koleksiyon ve işlemlere erişsin; bir enjeksiyon başarılı olsa bile etkisi sınırlı kalsın.
- Girdi doğrulamayı sunucuda yapın. İstemci-taraflı kontrol yeterli değildir; saldırgan isteği doğrudan API'ye gönderir.
Bu yaklaşım genel girdi-doğrulama disipliniyle örtüşür ve OWASP Top 10 web uygulama güvenliği çerçevesindeki enjeksiyon maddesinin NoSQL'e uygulanmış halidir.
KAOS ve DSET yaklaşımı
DSET olarak enjeksiyon testlerinde veritabanı türüne göre ayrım yapmayız; SQL kullanmayan bir sistem gördüğümüzde NoSQL operatör ve JavaScript enjeksiyonunu özellikle test ederiz, çünkü "NoSQL güvenli" yanılgısı bu açıkları sahada sık bırakır. Testlerimizde kimlik-doğrulama ve filtre uç noktalarına operatör nesneleri enjekte eder, $regex ile kör çıkarımı dener ve açığı yalnızca tespit etmez, atlatılan kontrolü veya sızdırılan veriyi kanıtlayarak doğrularız. Geliştirdiğimiz KAOS aracı, string beklenen alanlarda nesne/operatör girdisini kabul eden uç noktaları ve açık $where kullanımını işaretler, ama son kararı kanıta bağlar. Uygulamanızı NoSQL injection'a karşı test ettirmek için Ankara Hacettepe Teknokent'teki ekibimizle DSET ile iletişime geçin.
Sık sorulan sorular
MongoDB kullanıyorum, parametreli sorgu yok mu? NoSQL'de "parametreli sorgu"nun SQL'deki tam karşılığı farklı çalışır; temel ilke girdinin sorgu yapısını değil değeri belirlemesidir. Pratikte bu, alanları beklenen türe (string) zorlamak ve kullanıcı girdisini operatör olarak yorumlanamayacak biçimde kullanmakla sağlanır. Katı girdi doğrulama (nesne/dizi gelirse reddet) en etkili tek önlemdir.
$ne ile giriş atlatma gerçekten çalışır mı?
Sunucu istek gövdesindeki parola alanını tür kontrolü yapmadan sorguya koyuyorsa evet. {"$ne": null} sorguyu "parolası dolu olan kullanıcı" anlamına çevirir ve ilk eşleşen hesapla giriş yapılabilir. Çözüm basittir: parola alanı string değilse isteği hiç işlemeyin. Bu tek doğrulama bu saldırı sınıfının çoğunu kapatır.
NoSQL injection sadece MongoDB'de mi olur? Hayır. Kullanıcı girdisinin sorgu veya komut yapısına karışabildiği her NoSQL sisteminde (belge, anahtar-değer, grafik veritabanları) benzer riskler vardır; yalnızca operatör ve sözdizimi değişir. İlke her yerde aynıdır: girdiyi değer olarak kullanın, yapı olarak değil, ve sunucu-taraflı kod çalıştıran özellikleri kapatın.
Kaynaklar
- OWASP Web Security Testing Guide, testing for NoSQL injection bölümü.
- OWASP Top 10, A03 Injection maddesi (NoSQL dahil).
- CWE-943 (improper neutralization of special elements in data query logic).
- DSET blog: SQL injection nedir ve korunma ve OWASP Top 10 web uygulama güvenliği.
Kimliğinizi doğrulayın
Yetkilendirilmiş erişim alanı. Tüm giriş denemeleri kayıt altına alınır.