Aikido

Global değişkenler Node.js sunucularında neden veri sızıntılarına neden olur

Güvenlik

Kural
İstenmeyen global değişken önbelleklemesinden kaçının. Node.js
ve Python sunucularında global değişkenler istekler boyunca
kalıcılığını korur; bu da veri sızıntılarına ve race condition'lara yol açar.

Desteklenen diller: JavaScript, TypeScript, Python

Giriş

Node.js sunucularındaki global değişkenler, yalnızca tek bir isteklik süre boyunca değil, sürecin (process) yaşam döngüsü boyunca kalır. İstek işleyicileri (request handlers) kullanıcı verilerini global değişkenlerde sakladığında, bu verilere farklı kullanıcılardan gelen sonraki istekler üzerinden de erişilebilir. Bu durum, A kullanıcısının oturum verilerinin, kimlik doğrulama token'larının veya kişisel bilgilerinin B kullanıcısına sızdığı güvenlik açıklarına yol açar.

Neden önemli?

Güvenlik etkileri (veri sızıntıları): Kullanıcıya özel verileri önbelleğe alan global değişkenler, istekler arası veri sızıntılarına neden olur. Bir kullanıcının kimlik doğrulama durumu, oturum verileri veya kişisel bilgileri diğer kullanıcılar tarafından görülebilir hale gelir; bu da gizlilik ve güvenlik sınırlarını ihlal eder.

Race condition'lar: Birden fazla eşzamanlı (concurrent) istek aynı global değişkeni değiştirdiğinde, öngörülemeyen davranışların ortaya çıkma olasılığı yüksek olur. A kullanıcısının verileri, işlem devam ederken B kullanıcısının isteği tarafından üzerine yazılarak silinebilir; bu da yanlış hesaplamalara, bozulmuş durumlara (corrupted state) veya kullanıcıların birbirlerinin verilerini görmesine yol açabilir.

Hata ayıklama karmaşıklığı (Debugging complexity): Global değişken önbelleklemesinden kaynaklanan sorunların yeniden üretilmesi (reproduce) son derece zordur çünkü bunlar isteğin zamanlamasına ve eşzamanlılığa bağlıdır. Hatalar, yük altında üretim ortamında (production) aralıklı olarak ortaya çıkar ancak tek iş parçacıklı (single-threaded) geliştirme testlerinde nadiren kendini gösterir.

Bellek sızıntıları: Temizleme yapılmadan veri biriktiren genel (global) değişkenler zamanla sınırsız şekilde büyür. Her istek, genel önbelleklere veya dizilere daha fazla veri ekler; bu da sonunda sunucu belleğini tükatarak süreçlerin (process) yeniden başlatılmasını gerektirir.

Kod örnekleri

❌ Kural dışı:

let currentUser = null;
let requestData = {};

app.get('/profile', async (req, res) => {
    currentUser = await getUserById(req.userId);
    requestData = req.body;

    const profile = await buildUserProfile(currentUser);
    res.json(profile);
});

function buildUserProfile(user) {
    return {
        name: currentUser.name,
        data: requestData
    };
}

Neden yanlış: currentUser ve requestData genel değişkenleri istekler boyunca kalıcılığını korur. Birden fazla istek eşzamanlı olarak yürütüldüğünde, kullanıcı A'nın buildUserProfile() işlevi hâlâ çalışırken kullanıcı B'nin isteği currentUser değişkeninin üzerine yazabilir ve kullanıcı A'nın, kullanıcı B'nin verilerini görmesine neden olabilir.

✅ Kurallara uygun:

app.get('/profile', async (req, res) => {
    const currentUser = await getUserById(req.userId);
    const requestData = req.body;

    const profile = buildUserProfile(currentUser, requestData);
    res.json(profile);
});

function buildUserProfile(user, data) {
    return {
        name: user.name,
        data: data
    };
}

Bu neden önemli: İstekle ilgili tüm veriler, istek işleyicisinin (request handler) kapsamındaki yerel değişkenlerde saklanır. Her isteğin, diğer eşzamanlı isteklere sızamayacak izole bir durumu vardır. İşlevler, genel duruma erişmek yerine verileri parametreler aracılığıyla alır; böylece yarış durumları (race conditions) ortadan kalkar.

Sonuç

İstekle ilgili tüm verileri yerel değişkenlerde veya çerçeveniz (framework) tarafından sağlanan istek nesnelerinde tutun. Genel değişkenleri yalnızca yapılandırma, bağlantı havuzları veya salt okunur önbellekler gibi gerçekten paylaşılan durumlar için kullanın. Genel durum gerekli olduğunda, uygun eşzamanlılık kontrolleri kullanın ve verilerin hiçbir zaman kullanıcıya özel olmadığından emin olun.

Sık Sorulan Sorular

Sorularınız mı var?

Node.js'te genel değişkenleri kullanmak ne zaman güvenlidir?

Genel değişkenler, tüm istekler için geçerli olan salt okunur veriler (uygulama yapılandırması, veritabanı bağlantı havuzları, derlenmiş şablonlar veya paylaşılan yardımcı programlar) için güvenlidir. Asla isteğe veya kullanıcıya özel verileri global olarak saklamayın. Verileri global olarak önbelleğe almanız gerekiyorsa, uygun anahtarlamaya sahip olduğundan ve erişimin iş parçacığı açısından güvenli (thread-safe) olduğundan emin olun veya Redis gibi uygun önbellekleme çözümleri kullanın.

Açıkça global olmayan modül düzeyindeki değişkenler hakkında ne söyleyebiliriz?

Modül düzeyindeki değişkenler (dosya kapsamında const, let, var) Node.js'te tam olarak global değişkenler gibi davranır. Tüm istekler boyunca kalıcıdırlar ve tüm eşzamanlı istek işleyicileri tarafından paylaşılırlar. Aynı veri sızıntısı ve yarış durumu riskleri bunlar için de geçerlidir. Modül düzeyindeki değişkenlere de açık global değişkenler gibi aynı temkinle yaklaşın.

Ara yazılımlar (middleware) ve rota işleyicileri (route handlers) arasında verileri nasıl paylaşırım?

Çerçeveniz tarafından sağlanan istek nesnesi özelliklerini kullanın. Express, req.locals veya req üzerinde özel özellikler sağlar. Fastify ise request.decorateRequest() özelliğine sahiptir. Bu nesneler istek kapsamındadır ve istek tamamlandıktan sonra otomatik olarak temizlenerek istekler arası sızıntıları önler.

Singleton kalıpları ve sınıf örnekleri (class instances) hakkında ne düşünmeliyiz?

Modül düzeyindeki Singleton örnekleri global durumdur. İstekle ilgili veriler tutuyorlarsa aynı sorunlar geçerlidir. Singleton'ları durumsuz (stateless) olacak veya yalnızca yapılandırma tutacak şekilde tasarlayın. Durum bilgisi içeren işlemler için istek başına yeni örnekler oluşturun veya izolasyon sağlayan fabrika (factory) kalıpları kullanın.

Geliştirme sırasında bu sorunları nasıl tespit edebilirim?

Farklı kullanıcı bağlamları kullanan eşzamanlı isteklerle yük testleri çalıştırın. Yarış durumları ve veri sızıntıları genellikle ardışık testlerde ortaya çıkmaz. Eşzamanlı yük oluşturmak için Apache Bench veya autocannon gibi araçlar kullanın. Bir isteğe ait verilerin başka bir istekte ne zaman göründüğünü izlemek için istek kimliklerini (request ID) içeren günlükler (logging) ekleyin.

Bu durum AWS Lambda gibi sunucusuz (serverless) işlevler için de geçerli mi?

Kısmen. Her Lambda çağrısı yeni bir yürütme ortamı alır, ancak kapsayıcı (container) çağrılar arasında yeniden kullanılabilir. Genel değişkenler, aynı kapsayıcıyı yeniden kullanan çağrılar arasında kalıcılığını korur. Global değişkenlerin sıfırlanacağına güvenmeyin. Aynı uygulamaları izleyin: istek verilerini yerel kapsamda tutun.

Python WSGI/ASGI uygulamaları için durum nedir?

Aynı ilkeler geçerlidir. Python web sunucuları çok iş parçacıklı (multi-threaded) veya asenkron çalışır; bu nedenle modül düzeyindeki değişkenler istekler arasında paylaşılır. Flask'ın g nesnesi ve FastAPI'nin bağımlılık enjeksiyonu (dependency injection), istek kapsamlı depolama alanı sağlar. Django'da ise istek nesneleri bulunur. İstek verileri için modül genelleri yerine çerçeve tarafından sağlanan mekanizmaları kullanı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.