Skip to main content

Sicherheit des Sitzungsmanagements: Best Practices zum Schutz von Benutzersitzungen

Artikel von
blog feature pypi spoof

11. Juli 2023

0 Min. Lesezeit

Sicherheit beim Sitzungsmanagement ist ein wesentlicher Bestandteil der Entwicklung von Webanwendungen. Sie schützt Benutzersitzungen und verhindert unbefugten Zugriff. Ein sicheres Sitzungsmanagement gewährleistet die Vertraulichkeit, Integrität und Verfügbarkeit sensibler Benutzerdaten. Außerdem schützt es die Privatsphäre der Benutzer insgesamt – und trägt damit entscheidend dazu bei, ihr Vertrauen in eine Anwendung zu erhalten.

Wenn wir Sitzungen sicher verwalten, richten wir Prozesse ein, die Sitzungstoken löschen, sobald sich Benutzer abmelden oder ihre Sitzung endet. So wird unbefugter Zugriff durch Personen verhindert, die die Netzwerkverbindung abhören oder sich als jemand anderes ausgeben möchten. 

In diesem praxisorientierten Artikel erfahren Sie, wie Sie ein sicheres Sitzungsmanagement implementieren und welche Best Practices dabei zu beachten sind. 

Sicherheit beim Sitzungsmanagement verstehen

Um die Bedeutung des Sitzungsmanagements auf Domainebene kennenzulernen, entwickeln wir eine einfache Node.js-Anwendung mit grundlegenden Funktionen für Anmeldung, Abmeldung und Sitzungsmanagement. 

Voraussetzungen

Für dieses Tutorial benötigen Sie:

Beachten Sie, dass diese Demonstration das Betriebssystem Windows und die dazugehörigen Befehle verwendet. 

Eine Übersicht über das gesamte Projekt finden Sie im vollständigen Code.

Sitzungen in Node.js implementieren

Öffnen Sie zunächst die Eingabeaufforderung (CMD) und führen Sie den folgenden Code aus, um einen Ordner namens sessions zu erstellen, in diesen zu wechseln und die Anwendung zu initialisieren:

mkdir session 
cd sessions
npm init -y

Mit den obigen Befehlen wird automatisch eine package.json-Datei generiert, die alle im Projekt installierten Abhängigkeiten enthält. 

Als Nächstes installieren wir einige Pakete, die für die Umsetzung der genannten Ziele erforderlich sind. Dazu gehören express, ein Framework zum Erstellen von Webservern, und express-session, ein serverseitiges HTTP-Framework, das für die Erstellung von Sitzungs-Middleware benötigt wird. Installieren Sie beide Pakete mit folgendem Code:

npm i express express-session

Öffnen Sie nun das Projekt in Visual Studio Code.

Für die Anwendungslogik erstellen wir eine index.js-Datei. Führen Sie in CMD den Befehl touch index.js aus, um eine index.js-Datei zu erstellen. Aktualisieren Sie anschließend die index.js-Datei mit dem folgenden Code:

const express = require('express'); 
const session = require('express-session');

const  app = express();
const port = 3001;

// Session Middlewares, and Login and Logout Routes go here
app.use(session({
  secret: '38sisjsk92',
  resave: false,
  saveUninitialized: true
}));

// URL-encoded middleware
app.use(express.urlencoded({ extended: true }));

app.listen(port, () => {
  console.log(`Server listening on port ${port}`);
});

Das obige Codebeispiel enthält den Startcode für eine Node.js-Anwendung. Im Hauptteil unseres Projekts wird die Sitzungs-Middleware für die Express-App eingerichtet. 

Die URL-encoded-Middleware verarbeitet eingehende Anfragen mit URL-codierten Formulardaten in einer Express.js-Anwendung. 

Fügen Sie als Nächstes den folgenden Code unterhalb der Middleware in die index.js-Datei ein, um die An- und Abmeldefunktion einer Benutzersitzung zu verarbeiten:

// Routes
app.get('/', (req, res) => {
  if (req.session.username) {
	res.send(`Welcome to the page, ${req.session.username}! <a href="/logout">Logout</a>`);
  } else {
	res.send('Welcome, Please <a href="/login">login</a>.');
  }
  console.log(req.session)
});

app.get('/login', (req, res) => {
  if (req.session.username) {
	res.redirect('/');
  } else {
	res.send(`
  	  <form method="post" action="/login">
<input type="text" name="username" placeholder="Username" required>
    		<br /> <br />
    		<input type="text" name="password" placeholder="Password" required>
    		<br /> <br />
    		<button type="submit">Login</button>
  	  </form>
	`);
  }
});

// validate your username and password here
app.post('/login', (req, res) => {
  const { username, password } = req.body;
  req.session.username = username;
  req.password = password;
  res.redirect('/');
});

app.get('/logout', (req, res) => {
  req.session.destroy();
  res.redirect('/');
});

Sehen wir uns kurz an, was im obigen Code passiert:

  • Die erste Route, app.get('/'), verarbeitet Anfragen an die Root-URL der Anwendung. Sie begrüßt Benutzer, deren Benutzername in ihrer Sitzung gespeichert ist, und bietet ihnen einen Link zum Abmelden. Andernfalls werden sie aufgefordert, sich anzumelden.

  • Die zweite Route, app.get('/login'), verarbeitet Anfragen an die Anmelde-URL. 

  • Die dritte Route, app.post('/login'), verarbeitet das Absenden des Anmeldeformulars. 

  • Die vierte Route, app.get('/logout'), verarbeitet GET-Anfragen an die Abmelde-URL. Sie löscht die Sitzung des Benutzers und leitet ihn zur Root-URL weiter.

Speichern Sie alle Änderungen, wechseln Sie dann zum Terminal und führen Sie die Anwendung mit diesem Befehl aus:

node index.js

Da wir auf Port 3001 lauschen, öffnen Sie http://localhost:3001/ in einem Browser. Wenn Sie die URL aufrufen, sehen Sie diesen einfachen Anmeldelink:

Einfache Webseite mit der Nachricht „Willkommen! Bitte melden Sie sich an.“

Klicken Sie auf den Anmeldelink und geben Sie Ihren Namen ein. Daraufhin gelangen Sie zur folgenden Landingpage:

Webseite mit der Anzeige „Willkommen auf der Seite, John Doe!“ und einem Link „Abmelden“.

Sehen Sie sich nun das Terminal an. Dort wird das Sitzungsobjekt angezeigt, das so aussieht:

Terminal mit einem Node.js-Server, der auf Port 3001 lauscht, und einem Session-Objekt mit einem Cookie und dem Benutzernamen „John Doe“.

Die erste Eigenschaft ist der Pfad. Sie gibt den gültigen Pfad des Sitzungscookies an. Als Nächstes folgen der Ablaufzeitpunkt der Sitzung und die Gültigkeitsdauer des Cookies. Das Attribut httpOnly ist auf true gesetzt, um Client-Angriffe wie Cross-Site-Scripting (XSS) zu verhindern. Das Sitzungsobjekt zeigt außerdem den aktuellen Benutzernamen an.

Sitzungsmanagement und seine Rolle in Webanwendungen

Nachdem wir eine Beispielanwendung vorbereitet haben, sehen wir uns an, wie Sitzungsmanagement funktioniert und warum es für die sichere Verwaltung von Sitzungen so wichtig ist.

In Webanwendungen enthalten Interaktionen zwischen Benutzern und Anwendungen häufig sensible Informationen – in diesem Fall ein Passwort. Das Sitzungsmanagement gewährleistet, dass diese Interaktionen für authentifizierte Benutzer sicher sind und die während ihrer Sitzungen übermittelten Informationen stets geschützt bleiben. Geschieht dies nicht, kann es zu Schwachstellen im Sitzungsmanagement kommen. 

Zu den häufigen Schwachstellen gehören:

  • Sitzungsübernahme — Bei einer Sitzungsübernahme kompromittiert ein Angreifer ein Sitzungstoken, indem er es stiehlt oder errät, um im Namen des Besitzers unbefugten Zugriff auf den Server zu erlangen. Dafür gibt es verschiedene Methoden. Eine davon ist der Einsatz von XSS. Ist das httpOnly-Attribut in der aufgerufenen Webanwendung auf false gesetzt, ist das Risiko einer clientseitigen Sitzungsübernahme sehr hoch und XSS-Angriffen wird Tür und Tor geöffnet. 

  • Man-in-the-Middle-Angriffe (MITM) — Diese Angriffe sind eine weitere Form der Sitzungsübernahme. Ein Angreifer kann auf eine Sitzung zugreifen, indem er die Sitzungs-ID abfängt, während Benutzer und Server miteinander kommunizieren. Das ist möglich, wenn die Sitzungs-ID im Klartext übertragen wird und von einem Angreifer im Datenkanal leicht gelesen werden kann.

  • Session Fixation — Dabei bringt ein Angreifer den Benutzer dazu, eine bereits vorhandene Sitzungs-ID zu verwenden, um durch Übernahme der gültigen Benutzersitzung auf den Server zuzugreifen. Dies geschieht hauptsächlich, wenn die Identität des Benutzers, der auf die Sitzung zugreift, nicht ordnungsgemäß anhand der Sitzungs-ID überprüft wird. Es kann auch passieren, wenn ein Angreifer dem Opfer eine anfällige Sitzungs-ID in einer URL sendet.

Die Risiken unsicherer Sitzungsmanagementprozesse können erheblich sein. Mithilfe der erlangten sensiblen Daten können Kriminelle Betrug und Diebstahl begehen und auf vertrauliche Informationen zugreifen. Solche Sicherheitsverletzungen schaden den Benutzern erheblich und können ihr Vertrauen in eine Anwendung nachhaltig beeinträchtigen.

Glücklicherweise gibt es verschiedene Strategien, mit denen wir unsere Sitzungsmanagementprozesse absichern können. In den folgenden Abschnitten stellen wir einige Methoden vor und zeigen, wie Sie sie implementieren.

Sichere Cookies implementieren

Cookies können dazu beitragen, Sitzungen sicher zu verwalten. Beim Sitzungsmanagement verknüpfen Cookies Benutzer mit einem Sitzungsobjekt auf der Serverseite. Dank ihrer eindeutigen Kennung enthalten sie alle zugehörigen Aktivitäten des Benutzers über die von ihm besuchten Instanzen hinweg.

Wenn ein Benutzer beispielsweise auf eine Anwendung zugreift, um sich anzumelden, wird ein Sitzungsobjekt erstellt und ihm eine Sitzungs-ID zugewiesen. Das Cookie speichert die Sitzungs-ID und sendet sie als Antwort an den Browser. Stellt der Benutzer eine Anfrage, sendet der Browser das Cookie mit. Damit wird die Sitzungs-ID überprüft und das zugehörige Objekt abgerufen. So kann der Server den Benutzer validieren und seine Sitzung über verschiedene Seiten und Anfragen hinweg aufrechterhalten. 

Systementwickler, Anwendungsarchitekten und Administratoren sollten daher sicherstellen, dass Cookies so sicher wie möglich verwendet werden.

Cookies lassen sich beispielsweise mit einem Secure-Flag schützen, das Anfragen ausschließlich über HTTPS zulässt. Es schützt Benutzer im Netzwerk vor einem MITM-Angriff im Datenpfad, da das übertragene Paket nicht im Klartext vorliegt und somit nicht gelesen werden kann.

Neben MITM-Angriffen sind Cookies auch für XSS-Angriffe anfällig, da sie Datenpakete über das Netzwerk übertragen. Mit einem HttpOnly-Flag, das JavaScript daran hindert, das Sitzungscookie zu lesen, können wir solche Angriffe verhindern. 

Um dieses Flag zu implementieren, müssen wir prüfen, ob die Eigenschaft HttpOnly des Sitzungsobjekts auf den booleschen Wert true gesetzt ist. 

Öffnen Sie Ihr Terminal und installieren Sie mit dem folgenden Befehl cookie-parser, die Middleware zum Speichern Ihrer Cookies, sowie bcrypt, die anfällige Paketversion:

npm i cookie-parser bcrypt@v4.0.1

Aktualisieren Sie anschließend den Code in der index.js-Datei wie folgt:

const cookieParser = require('cookie-parser');

// Enable cookie parsing
app.use(cookieParser());

// Set up session middleware
app.use(session({
  secret: '38sisjsk92',
  resave: false,
  saveUninitialized: true,
  cookie: {
	httpOnly: true, 
	secure: false, // Sent only over HTTPS if true
  },   
}));

Damit erkannte Schwachstellen in Ihrem Code in Visual Studio Code angezeigt werden, muss Snyk Code in Ihrem Konto aktiviert sein. Sie können die Funktion über die Visual Studio Code-Erweiterung oder die Snyk-Website aktivieren. Wenn Sie im Visual Studio Code-Terminal auf den Tab PROBLEMS schauen, sehen Sie, dass Snyk mehrere Schwachstellen im Code hervorgehoben hat:

Problems-Bereich in VS Code mit fünf Sicherheitsproblemen in einer Express-App, darunter fest codierte Geheimnisse, deaktivierter CSRF-Schutz und Cookie-Einstellungen.

Der Scan zeigt ein fest im Code hinterlegtes Secret, deaktivierten Cross-Site-Request-Forgery-Schutz (CSRF), ein deaktiviertes Secure-Flag und ein anfälliges Paket.

Snyk schlägt anhand der angezeigten Hinweise mehrere Möglichkeiten vor, diese Probleme zu beheben. Beheben wir einige davon. 

Zunächst empfiehlt Snyk, fest codierte Werte zu vermeiden und stattdessen Umgebungsvariablen zu verwenden. Führen Sie dazu im Terminal die folgenden Befehle aus:

npm i dotenv
touch .env

Mit dem ersten Befehl wird das Paket dotenv zum Auslesen der festgelegten Umgebungsvariablen installiert. Der zweite Befehl erstellt eine Umgebungsdatei, in der solche vertraulichen Informationen gespeichert werden. 

Fügen Sie nun diese Konfiguration zur Datei .env hinzu:

SESSION_SECRET=38sisjsk92

Aktualisieren Sie anschließend die Sitzungs-Middleware in der Datei index.js mit diesem Code:

require('dotenv').config(); // loads env variables 
app.use(session({
  secret: process.env.SESSION_SECRET,
}));

Durch diese Änderungen wird sichergestellt, dass das Secret aus der Umgebungsvariable gelesen wird. 

Setzen Sie als Nächstes das Secure-Flag auf true.

Da die Aktivierung von CSRF zusätzlichen Code für unsere Anwendung erfordern würde, lassen wir diesen Schritt in dieser Demonstration aus. Denken Sie jedoch daran, dies in Ihren eigenen Anwendungen mit dem csrf-Paket umzusetzen.

Um das Problem mit dem anfälligen Paket zu beheben, besuchen Sie die offizielle npm-Dokumentation und installieren Sie eine aktualisierte Version oder das besser getestete und sichere bcryptjs-Paket. 

Nachdem die Snyk-Erweiterung Ihre Anwendung nach diesen Aktualisierungen gescannt hat, werden Sie feststellen, dass die meisten Fehler verschwunden sind. Wenn Sie die Anwendung jetzt ausführen, funktioniert localhost nicht ohne HTTPS und leitet Sie immer wieder zur Root-URL weiter. 

Neben der Verwendung des lokalen Speichers zum Speichern von Client-Anfragen können Sie die Sitzungsdaten Ihrer Anwendung auch in einer Datenbank speichern. So können Sie den Server neu starten, ohne dass die Sitzungsdaten verloren gehen. Da sie anhand einer eindeutigen Sitzungs-ID gespeichert werden, können sich Benutzer problemlos anmelden, ohne jedes Mal ihre Zugangsdaten eingeben zu müssen. Alternativ können Sie Speichercaches wie Redis verwenden. 

Sitzungs-Timeouts implementieren

Ein Sitzungs-Timeout tritt ein, wenn die Sitzung eines Benutzers abläuft und dieser automatisch vom System abgemeldet wird. Dadurch wird der Zeitraum begrenzt, in dem ein Sitzungsangriff stattfinden kann, und sichergestellt, dass die Sitzung nach einer festgelegten Zeit beendet wird.

Bei der Festlegung eines Sitzungs-Timeouts sollten Sie mehrere Faktoren berücksichtigen:

  • Anwendungstyp — Legen Sie die Dauer von Sitzungs-Timeouts entsprechend der Art der Daten fest, die Ihre Anwendung verarbeitet.

  • Benutzerverhalten — Stellen Sie sicher, dass Timeouts die Benutzererfahrung nicht beeinträchtigen.

Wir implementieren Sitzungs-Timeouts in der Sitzungs-Middleware. Aktualisieren Sie den Code mit dem folgenden Ausschnitt, um das Sitzungs-Timeout auf eine Stunde festzulegen:

// Set up session middleware
app.use(session({
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: true,
  cookie: {
	maxAge: 3600000, // Expires after 1 hour (in milliseconds)
	httpOnly: true, // Accessible only via HTTP(S)
	secure: false, // Sent only over HTTPS
  },   
}));

Eine ordnungsgemäße Abmeldefunktion implementieren

Mit einer Abmeldefunktion können Benutzer ihre Sitzungen ausdrücklich beenden, wenn sie eine Anwendung nicht mehr verwenden. So behalten sie die Kontrolle darüber, wann ihre Cookie-Sitzungen enden, und gewinnen ein Gefühl der Eigenverantwortung für ihre Sicherheit.

Die Abmeldefunktion erfordert jedoch weiterhin Aufmerksamkeit von Entwicklerinnen und Entwicklern. Wenn sich ein Benutzer abmeldet, wird die Sitzung auf der Client-Seite möglicherweise beendet. Auf der Serverseite wurde das Sitzungscookie jedoch unter Umständen nicht vollständig ungültig gemacht. Angreifer können diese Sitzungstokens verwenden und Aktionen ausführen, als wären sie die ursprünglichen Benutzer.

Um diese Sicherheitslücke zu verhindern, müssen wir sicherstellen, dass das Sitzungstoken und die Sitzungs-ID des Benutzers auf der Serverseite gelöscht werden. Implementieren Sie diese Funktion in der Anwendung mithilfe des folgenden Codes als letzte Route in der Datei index.js:

app.get('/logout', (req, res) => {
  req.session.destroy(err => {
	if (err) {
  	  console.log(err)
	} else {
  	  res.clearCookie('connect.sid');
  	  res.redirect('/');
	}
  });
});

Dieser Code ruft destroy() mit einer Callback-Funktion auf, um die Sitzung auf der Serverseite zu löschen, wenn sich ein Benutzer abmeldet. Außerdem ruft er clearCookie() auf und löscht connection.sid – einen Schlüssel, der dem Wert des angemeldeten Benutzers zugewiesen ist.

Wenn Sie die Cookies Ihres angemeldeten Benutzers im Browser über die Untersuchungsfunktion prüfen, sollte connection.sid angezeigt werden, wie unten dargestellt:

Browser-Entwicklertools mit ausgewähltem localhost-connect.sid-Sitzungscookie im Cookies-Bereich.

Mit den oben beschriebenen Schritten können wir sicherstellen, dass sich Benutzer des Systems abmelden können, ohne sich Gedanken um ihre Online-Sicherheit machen zu müssen. Mit der Snyk Code-Erweiterung können Sie alle in unserem Projekt durchgeführten Schwachstellen-Scans und deren jeweiligen Status anzeigen. So können wir sicherstellen, dass die Anwendung frei von Sicherheitsrisiken ist, bevor wir sie für Benutzer bereitstellen.

In diesem Artikel haben wir unser Projekt mit Snyk gescannt. Dabei haben wir erfahren, wie sich Schwachstellen wie hartcodierte Geheimnisse, Sitzungsprobleme und die Verwendung eines anfälligen Pakets beheben lassen. Nachdem wir die von Snyk erkannten Schwachstellen behoben haben, weist unser System keine mehr auf, wie der folgende Bericht zeigt:

Dunkles Snyk-Dashboard, das keine Schwachstellen oder Probleme in Open-Source-, Code-, Konfigurations- und Codequalitätsscans anzeigt.

Weitere Best Practices für die sichere Sitzungsverwaltung

Zusätzlich zu den in diesem Artikel vorgestellten Verfahren finden Sie hier einige weitere Best Practices für eine sicherere Sitzungsverwaltung:

  • Sichere Sitzungskennungen verwenden — Sichere Sitzungskennungen sind lange Zeichenfolgen aus eindeutigen Zeichen, die zur Authentifizierung von Benutzersitzungen verwendet werden. Diese Kennungen lassen sich automatisch generieren. Durch ihre Zufälligkeit und Unvorhersehbarkeit sind sie für selbst akribische Angreifer schwer zu erraten – selbst mit statistischen Analysemethoden. Außerdem ist die Zeichenfolge lang genug, um Brute-Force-Angriffen standzuhalten.

  • Sitzungen serverseitig verwalten — Das Erfassen von Sitzungskennungen auf der Client-Seite kann trotz ihrer eindeutigen Generierung zu einer Schwachstelle führen. Mit einer serverseitigen Sitzungsverwaltung können wir diese Kennungen speichern und den Benutzern in der Anwendung zuordnen. Bei dieser Art der Sitzungsspeicherung sind vertrauliche Zugangsdaten der Benutzer deutlich besser geschützt. So können sie das System bedenkenlos nutzen, ohne befürchten zu müssen, dass sich jemand als sie ausgibt.

  • Abmeldungen ordnungsgemäß überprüfen — Meldet sich ein Benutzer bei einer Instanz an und anschließend ab, sollte das System die Abmeldung auf allen vom Benutzer besuchten Instanzen ordnungsgemäß umsetzen. Das kann kompliziert sein, wenn die Sitzungsdaten nicht zwischen den Instanzen synchronisiert werden. Trotzdem ist es wichtig, denn Instanzen können Angreifern als Einfallstor dienen, um Benutzerinformationen zu stehlen.

Fazit

In diesem Artikel haben wir verschiedene Strategien zur Verbesserung der Sicherheit der Sitzungsverwaltung untersucht, um Besucher bestmöglich zu schützen und die Zahl der Angriffe zu verringern. Eine sichere Sitzungsverwaltung schützt persönliche Informationen, Daten und Konten – auch wenn Benutzer persönliche Spuren hinterlassen.

Sie schützt Benutzer auch bei der Interaktion mit unseren Websites und Webanwendungen. Durch die Umsetzung der hier beschriebenen Best Practices und Sicherheitsstrategien können wir unseren Benutzern die Gewissheit geben, dass ihre Zugangsdaten und vertraulichen Informationen sicher sind.

Starten Sie mit Capture the Flag

Erfahren Sie in unserem virtuellen On-Demand-Workshop für Einsteiger, wie Sie Capture-the-Flag-Herausforderungen lösen.

Weiterlesen

feature insights context
Blog

Autonome Angriffe sind bereits Realität. Die Verteidigung muss Schritt halten.

Autonome Angreifer verkürzen das Zeitfenster für die Verteidigung. Erfahren Sie, wie kontinuierliches Erkennen, Beheben, Validieren und Verhindern Sicherheitsteams helfen kann, Schritt zu halten.

illustration hero ai
Blog

Der KI-Hurrikan ist da

KI beschleunigt gleichermaßen die Softwareentwicklung und Cyberangriffe. Führungskräfte müssen Agents und Code von Anfang an absichern, zur Laufzeit Kontrollen durchsetzen und Abwehrmaßnahmen unabhängig validieren.

feature insights context
Blog

Ist Prävention im Grunde ein gelöstes Problem?

Die Prävention in von Agenten generiertem Code ist architektonisch gelöst – die Herausforderung besteht jedoch weiterhin darin, Kontrollen zu wählen, die die Sicherheit schützen, ohne die Entwicklung zu verlangsamen.