Aikido

Warum globale Variablen Datenlecks in Node.js-Servern verursachen

Sicherheit

Regel
Vermeiden Sie unbeabsichtigte globale Variable caching.In Node.js
und Python Server, globale Variablen bleiben bestehen über
Anfragen, verursacht Daten Datenlecks und Rennen Bedingungen.

Unterstützte Sprachen: JavaScript, TypeScript, Python

Einführung

Globale Variablen in Node.js-Servern bleiben für die gesamte Dauer des Prozesses bestehen, nicht nur für eine einzelne Anfrage. Wenn Request-Handler Benutzerdaten in globalen Variablen speichern, bleiben diese Daten für nachfolgende Anfragen von verschiedenen Benutzern zugänglich. Dies führt zu Sicherheitslücken, bei denen die Sitzungsdaten, Authentifizierungs-Tokens oder persönlichen Informationen von Benutzer A an Benutzer B weitergegeben werden.

Warum das wichtig ist

Auswirkungen auf die Sicherheit (Datenlecks): Globale Variablen, die benutzerspezifische Daten zwischenspeichern, verursachen anfrageübergreifende Datenlecks. Der Authentifizierungsstatus eines Benutzers, Sitzungsdaten oder persönliche Informationen werden für andere Benutzer sichtbar und verletzen so die Datenschutz- und Sicherheitsgrenzen.

Wettlaufbedingungen: Wenn mehrere gleichzeitige Anfragen dieselbe globale Variable ändern, besteht eine hohe Wahrscheinlichkeit für unvorhersehbares Verhalten. Die Daten von Benutzer A können während der Verarbeitung von der Anfrage von Benutzer B überschrieben werden, was zu falschen Berechnungen, einem beschädigten Status oder dazu führt, dass die Benutzer die Daten der anderen sehen.

Komplexität der Fehlersuche: Probleme, die durch das Zwischenspeichern globaler Variablen verursacht werden, sind bekanntermaßen schwer zu reproduzieren, da sie vom Timing der Anfragen und der Gleichzeitigkeit abhängen. Fehler treten in der Produktion unter Last sporadisch auf, werden aber bei Single-Thread-Entwicklungstests selten sichtbar.

Speicherlecks: Globale Variablen, in denen sich Daten ansammeln, ohne dass sie bereinigt werden, wachsen mit der Zeit ins Unermessliche. Jede Anfrage fügt mehr Daten zu den globalen Caches oder Arrays hinzu, was schließlich den Serverspeicher erschöpft und einen Neustart des Prozesses erfordert.

Code-Beispiele

❌ Nicht konform:

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
    };
}

Warum das falsch ist: Die globalen Variablen currentUser und requestData bleiben über Anfragen hinweg bestehen. Wenn mehrere Anfragen gleichzeitig ausgeführt werden, kann die Anfrage von Benutzer B currentUser überschreiben, während buildUserProfile() von Benutzer A noch ausgeführt wird, wodurch Benutzer A die Daten von Benutzer B sieht.

✅ Konform:

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
    };
}

Warum dies wichtig ist: Alle anfragespezifischen Daten werden in lokalen Variablen gespeichert, die auf den Request-Handler beschränkt sind. Jede Anfrage hat einen isolierten Zustand, der nicht an andere gleichzeitige Anfragen weitergegeben werden kann. Funktionen erhalten Daten über Parameter, anstatt auf den globalen Zustand zuzugreifen, wodurch Race Conditions vermieden werden.

Schlussfolgerung

Bewahren Sie alle anfragespezifischen Daten in lokalen Variablen oder in von Ihrem Framework bereitgestellten Anfrageobjekten auf. Verwenden Sie globale Variablen nur für wirklich gemeinsam genutzte Zustände wie Konfiguration, Verbindungspools oder schreibgeschützte Caches. Wenn ein globaler Zustand notwendig ist, verwenden Sie geeignete Gleichzeitigkeitskontrollen und stellen Sie sicher, dass die Daten niemals benutzerspezifisch sind.

FAQs

Haben Sie Fragen?

Wann ist es sicher, globale Variablen in Node.js zu verwenden?

Globale Variablen sind sicher für schreibgeschützte Daten, die für alle Anfragen gelten: Anwendungskonfiguration, Datenbankverbindungspools, kompilierte Vorlagen oder gemeinsam genutzte Dienstprogramme. Speichern Sie niemals anfragespezifische oder benutzerspezifische Daten global. Wenn Sie Daten global zwischenspeichern müssen, stellen Sie sicher, dass sie richtig verschlüsselt sind und der Zugriff thread-sicher ist, oder verwenden Sie geeignete Caching-Lösungen wie Redis.

Was ist mit Variablen auf Modulebene, die nicht explizit global sind?

Variablen auf Modulebene (const, let, var auf Dateiebene) verhalten sich genau wie Globals in Node.js. Sie bleiben über alle Anfragen hinweg bestehen und werden von allen gleichzeitigen Request-Handlern gemeinsam genutzt. Es gelten die gleichen Risiken für Datenlecks und Wettlaufbedingungen. Behandeln Sie Variablen auf Modulebene mit der gleichen Vorsicht wie explizite Globals.

Wie kann ich Daten zwischen Middleware und Route-Handlern austauschen?

Verwenden Sie die von Ihrem Framework bereitgestellten Eigenschaften des Anfrageobjekts. Express bietet req.locals oder benutzerdefinierte Eigenschaften für req. Fastify hat request.decorateRequest(). Diese Objekte sind anforderungsspezifisch und werden nach Abschluss der Anforderung automatisch aufgeräumt, um Lecks zwischen den Anforderungen zu vermeiden.

Was ist mit Singleton-Mustern und Klasseninstanzen?

Singleton-Instanzen auf Modulebene sind globale Zustände. Wenn sie anforderungsspezifische Daten enthalten, gelten die gleichen Probleme. Entwerfen Sie Singletons so, dass sie zustandslos sind oder nur die Konfiguration enthalten. Für zustandsbehaftete Operationen erstellen Sie neue Instanzen pro Anfrage oder verwenden Sie Factory-Patterns, die eine Isolierung gewährleisten.

Wie kann ich diese Probleme während der Entwicklung erkennen?

Führen Sie Lasttests mit gleichzeitigen Anforderungen unter Verwendung verschiedener Benutzerkontexte durch. Race Conditions und Datenlecks treten bei sequenziellen Tests oft nicht auf. Verwenden Sie Tools wie Apache Bench oder autocannon, um gleichzeitige Last zu erzeugen. Fügen Sie eine Protokollierung hinzu, die Anforderungs-IDs enthält, um zu verfolgen, wann Daten von einer Anforderung in einer anderen erscheinen.

Gilt dies auch für serverlose Funktionen wie AWS Lambda?

Teilweise. Jeder Lambda-Aufruf erhält eine neue Ausführungsumgebung, aber der container kann zwischen Aufrufen wiederverwendet werden. Globale Variablen bleiben zwischen Aufrufen bestehen, die denselben container wiederverwenden. Verlassen Sie sich nicht darauf, dass Globals zurückgesetzt werden. Befolgen Sie die gleichen Praktiken: Halten Sie die Anfragedaten im lokalen Bereich.

Was ist mit Python WSGI/ASGI-Anwendungen?

Es gelten dieselben Grundsätze. Python-Webserver werden mit mehreren Threads oder asynchron ausgeführt, so dass Variablen auf Modulebene über Anfragen hinweg gemeinsam genutzt werden. Das g-Objekt von Flask und die Dependency Injection von FastAPI bieten anforderungsspezifische Speicherung. Django hat Request-Objekte. Verwenden Sie vom Framework bereitgestellte Mechanismen anstelle von Modulglobalen für Anfragedaten.

Starten Sie kostenlos

Sichern Sie Ihren Code, Cloud und die Laufzeit in einem zentralen System.
Finden und beheben Sie Schwachstellen schnell  automatisch.

Keine Kreditkarte erforderlich | Scanergebnisse in 32 Sekunden.