- JWT ermöglicht zustandslose Authentifizierung in Node.js-APIs, wodurch der Bedarf an Serversitzungen reduziert und die Skalierbarkeit verbessert wird.
- Die Kombination von gehashten Passwörtern mit bcrypt, JWT-Verifizierungs-Middleware und HTTPS stärkt die Endpunktsicherheit.
- Die kombinierte Verwendung von kurzlebigen Zugriffstoken und Aktualisierungstoken ermöglicht lange Sitzungen mit zentralisierter Widerrufsmöglichkeit.
- Eine gute Verwaltung von Schlüsseln, Rollen, Ablaufdaten und Auditierung macht JWT zu einer Schlüsselkomponente moderner, auf Microservices basierender Architekturen.
Wer mit JavaScript-APIs arbeitet, kommt früher oder später an der Authentifizierung vorbei. Die Absicherung von Endpunkten in einer Node.js-API mit JWT hat sich nahezu zum De-facto-Standard entwickelt: Es ist ressourcenschonend, skalierbar und passt perfekt zu modernen Microservices-Architekturen oder Single-Page-Anwendungen (SPAs).
In diesem Artikel fügen wir alle Puzzleteile zusammen und betrachten praxisnah und detailliert, wie man die Authentifizierung mit JSON Web Tokens in einer mit Node.js und Express erstellten REST-API implementiert . Wir beginnen mit den wichtigsten Konzepten und enden mit fortgeschritteneren Mustern wie Refresh-Tokens , Routenschutz mit Middleware und einigen Sicherheitsempfehlungen, die beachtet werden sollten.
Was ist JWT und warum wird es in Node.js-APIs so häufig verwendet?
JSON Web Token (JWT) ist ein offener Standard, der ein kompaktes Format für die Informationsübertragung zwischen zwei Parteien als digital signiertes JSON-Objekt definiert. Diese Signatur ermöglicht die Überprüfung, ob die Nachricht unverändert ist und tatsächlich von der angegebenen Quelle stammt, ohne dass der Server einen Zustand speichern muss.
Ein JWT-Token besteht aus drei durch Punkte getrennte Abschnitte: header.payload.signature, üblicherweise in Base64 kodiert, was zu Zeichenketten wie diesen führt (sehr typisch, wenn man anfängt, mit jwt.io herumzuexperimentieren): Ein Teil ist der Header, ein anderer enthält die Benutzerdaten und ein dritter die Signatur. wodurch die Integrität des Ganzen bescheinigt wird.
Der erste Teil, der KopfzeileDies gibt in erster Linie den Token-Typ und den Signaturalgorithmus an. In Node.js-APIs findet man typischerweise etwas wie das hier. alg: HS256 y typ: JWTDas heißt, ein JWT-Token, das mit HMAC unter Verwendung von SHA-256 signiert wurde. Dieser Header ist in Base64 kodiert. und wird zum ersten Segment des Tokens.
Der zweite Abschnitt ist der Nutzlast: XNUMX KgDort werden die Informationen oder „Ansprüche“ aufgeführt. Dies ist üblicherweise der Bereich, in dem die Kennung, der Name und in vielen Fällen auch die Daten des Nutzers angegeben sind. grundlegende Rollen und Berechtigungen, zusätzlich zu Standardfeldern wie exp (Verfallsdatum), iat (Ausgabedatum) oder sub (Subjekt des Tokens). Es ist entscheidend zu verstehen, dass alles, was Sie hier eingeben, auch wenn es in Base64 kodiert ist, es ist nicht verschlüsselt, nur kodiert.
Der dritte Teil ist die Signatur . Sie wird erstellt, indem ein HMAC-Algorithmus, beispielsweise HMAC-SHA256, auf die Base64-kodierte Verkettung von Header und Payload sowie ein nur dem Server bekanntes Geheimnis angewendet wird . Mithilfe dieser Signatur kann überprüft werden, ob das Token während des Übertragungsvorgangs manipuliert wurde.
Der typische Ablauf in einer Node.js-API ist sehr einfach: Der Benutzer authentifiziert sich mit seinen Zugangsdaten, der Server überprüft deren Richtigkeit und antwortet mit einem signierten JWT. Ab diesem Zeitpunkt… alle Anfragen an geschützte Endpunkte Sie müssen das Token einbinden (zum Beispiel im Header). Authorization: Bearer <token>), und das Backend validiert dies bei jeder Anfrage, ohne eine Sitzung im Speicher oder in der Datenbank zu konsultieren.
Tokenbasierte Authentifizierung vs. traditionelle Sitzungen
In herkömmlichen sitzungsbasierten Systemen speichert der Server Informationen über jeden authentifizierten Benutzer (im Arbeitsspeicher, in Redis, MongoDB usw.) und verknüpft diese mit einem Cookie. Jede Anfrage fragt diese Informationen ab, um die Identität des Benutzers zu ermitteln. Dieser Ansatz funktioniert zwar, führt aber zu Problemen hinsichtlich Overhead, Skalierbarkeit und Synchronisierung zwischen Instanzen.
Mit JWT und zustandsloser Authentifizierung ändert sich dies: Der Server speichert die Sitzung nicht mehr; stattdessen sind alle zur Identifizierung des Benutzers benötigten Informationen im Token enthalten . Solange die Signatur gültig und das Token nicht abgelaufen ist, kann jede Instanz des Dienstes die Anfrage bearbeiten, ohne Sitzungen teilen zu müssen.
Dieses Design bietet mehrere klare Vorteile: bessere horizontale Skalierbarkeit (Sitzungen müssen nicht dupliziert werden), die Möglichkeit für verschiedene Anwendungen (Web, Mobil, Desktop), dieselbe API zu nutzen, und eine Verringerung bestimmter Schwachstellen im Zusammenhang mit der Sitzungsverwaltung, wie z. B. klassische CSRF- Angriffe auf Basis von Cookies.
Die Verwendung von JWT ist jedoch kein Allheilmittel. HTTPS, ein gutes Schlüsselmanagement und robuste Zugriffskontrollen sind weiterhin erforderlich . Darüber hinaus müssen Sie sorgfältig abwägen, welche Daten Sie in die Nutzdaten aufnehmen: Zu viele Informationen oder sensible Daten können negative Folgen haben, wenn jemand das Token abfängt oder stiehlt.
In komplexeren Umgebungen, insbesondere bei der Einführung von OAuth2 oder Integrationen von Drittanbietern, ist es üblich, das JWT durch Refresh-Token zu ergänzen , um den Zugriff zu erneuern, ohne alle paar Minuten nach Anmeldeinformationen fragen zu müssen, und so lange, aber sichere Sitzungen aufrechtzuerhalten.
In komplexeren Umgebungen, insbesondere bei der Einführung von OAuth2 oder Integrationen von Drittanbietern, ist es üblich, das JWT durch Refresh-Token zu ergänzen , um den Zugriff zu erneuern, ohne alle paar Minuten nach Anmeldeinformationen fragen zu müssen, oder Identitätsanbieter wie Firebase zu verwenden , um lange, aber sichere Sitzungen aufrechtzuerhalten.
Ersteinrichtung eines Node.js-Projekts mit Express und JWT
Um das alles in der Praxis zu sehen, legen wir den Grundstein für ein einfaches Projekt mit Node.js und Express . Die Idee ist, eine kleine REST-API zu entwickeln, die Registrierung, Login und Zugriff auf JWT-geschützte Routen ermöglicht. Wir beginnen mit einem einfachen Beispiel, das ein In-Memory-Array verwendet, und verbinden es anschließend mit Datenbanken wie MongoDB.
In einem leeren Verzeichnis initialisiert man üblicherweise das Projekt und fügt die grundlegenden Abhängigkeiten hinzu. Mindestens benötigt man Express für den HTTP-Server , jsonwebtoken zum Erstellen und Überprüfen von Tokens , bcryptjs zum Verschlüsseln von Passwörtern und – dringend empfohlen – dotenv zur Verwaltung von Umgebungsvariablen wie dem geheimen Schlüssel des Tokens oder der Datenbankverbindungszeichenfolge.
Damit haben Sie bereits das Grundgerüst, um eine Hauptdatei zu erstellen, zum Beispiel index.js o app.jsUm einen einfachen Express-Server einzurichten, aktivieren Sie JSON im Anfragetext parsen und die ersten Testrouten vorbereiten. Es ist ein Schritt ähnlich dem typischen „Hello World“-Programm, jedoch so konzipiert, dass es bald Benutzer und Token verwalten kann.
Es ist ratsam, von Anfang an die minimale Projektstruktur festzulegen: einen Routenordner zur Trennung von Authentifizierungs- und Geschäftsendpunkten, einen weiteren Modelleordner für Benutzerschemas, falls Sie Mongo/Mongoose verwenden möchten, und vielleicht einen Middlewaresordner für die JWT-Validierungslogik und andere Filter.
Parallel dazu empfiehlt es sich, eine Datei zu erstellen. .env zum Speichern des JWT-Geheimschlüssels, des Serverports, der Datenbank-URI und anderer Parameter. Dank dotenv. Sie müssen diese sensiblen Daten nicht in Ihrem Repository offenlegen.was unerlässlich ist, wenn Sie den Code anschließend auf GitHub hochladen oder auf einer PaaS-Plattform wie Heroku bereitstellen.
Grundstruktur des Express-Servers und Benutzersimulation
Sobald das Grundgerüst steht, beginnt man üblicherweise mit etwas sehr Einfachem: einem In-Memory-Array, das eine Benutzerdatenbank simuliert . Obwohl es nicht für den Produktiveinsatz geeignet ist, eignet es sich hervorragend, um den Ablauf von Registrierung, Anmeldung und Token-Ausgabe zu verstehen, ohne sich in einem komplexen Persistenzsystem zu verlieren.
Der erste wichtige Endpunkt ist die Registrierung . Nach Erhalt von Benutzername und Passwort muss das Backend überprüfen, ob der Benutzer bereits existiert, und – was am wichtigsten ist – das Passwort mit bcryptjs verschlüsseln, bevor es im Array oder in der Datenbank gespeichert wird. Passwörter dürfen unter keinen Umständen im Klartext gespeichert werden. Sowohl Benutzern als auch Administratoren wird die Verwendung sicherer Passwortverwaltungstools empfohlen.
Das Hashing mit bcrypt erfordert die Generierung eines „Salts“ und die Durchführung mehrerer Berechnungsrunden. Dadurch ist es rechenaufwändig, das ursprüngliche Passwort zu knacken , selbst wenn die Datenbank in falsche Hände gerät. Wenn sich ein Benutzer später anmeldet, wird das eingegebene Passwort daher mit dem mithilfe von bcrypt gespeicherten Hash verglichen, ohne dass das Originalpasswort jemals preisgegeben wird.
Der zweite grundlegende Endpunkt ist die Anmeldung . Wenn der Client seine Anmeldedaten sendet, sucht der Server den Benutzer im entsprechenden Array oder der entsprechenden Sammlung. Wird er gefunden, vergleicht er das gesendete Passwort mit dem gespeicherten Hash . Stimmen alle Daten überein, wird mithilfe der jsonwebtoken -Bibliothek ein JWT generiert . Dieses enthält üblicherweise mindestens die Benutzerkennung und einige grundlegende Daten.
An dieser Stelle wird das Token üblicherweise mit einem in den Umgebungsvariablen definierten geheimen Schlüssel signiert und eine Gültigkeitsdauer, beispielsweise eine Stunde, hinzugefügt. Der Server antwortet mit einem JSON-Objekt, das typischerweise das Token und gegebenenfalls auch Benutzerinformationen enthält , die für das Frontend nützlich sein können (Name, E-Mail-Adresse, Rolle usw.).
Schließlich müssen die Routen geschützt werden. Dazu ist ein Authentifizierungs-Middleware mit JWT das Token aus dem Header (oder aus einer anderen Quelle wie einem Cookie oder dem Header) liest x-auth-tokenÜberprüfen Sie die Signatur mithilfe des JSON-Web-Tokens und fügen Sie, falls korrekt, die Benutzerinformationen dem Objekt hinzu. req bevor zur nächsten Funktion in der Kette übergegangen wird.
Routenschutz mit JWT-Middleware in Express
Der Schlüssel zu einer sicheren API liegt in wiederverwendbarer Middleware zur Token-Validierung . Das Prinzip ist einfach: Bevor der Controller einer privaten Route erreicht wird, wird eine Funktion ausgeführt, die die Anfrage prüft, nach einem Token sucht, dieses verifiziert und erst dann den Zugriff gewährt.
In der Praxis liest diese Middleware üblicherweise den Header. Authorization auf der Suche nach einem Muster Bearer <token>oder vielleicht eine benutzerdefinierte Kopfzeile wie x-auth-tokenFalls Sie nichts finden, antwortet mit einem 401- oder 403-Fehler Dies bedeutet, dass die Anfrage nicht autorisiert ist oder keine gültigen Anmeldeinformationen enthält.
Wenn ein Token vorhanden ist, besteht der nächste Schritt darin, anzurufen jwt.verify mit dem Token und dem Geheimnis. Schlägt die Verifizierung aufgrund eines fehlerhaften, abgelaufenen oder manipulierten Tokens fehl, wird ein weiterer 401/403-Fehler zurückgegeben. Im Erfolgsfall wird die ursprüngliche Nutzlast abgerufen und kann gespeichert werden. req.user damit nachfolgende Controller wissen, wer der authentifizierte Benutzer ist.
Vor diesem Hintergrund wird jede Route, die Sie schützen möchten, einfach durch Hinzufügen der Middleware zu ihrer Definition konfiguriert. Zum Beispiel eine Route /dashboard o /clients Es werden nur dann Informationen zurückgegeben, wenn das Token gültig ist. Wenn das Token fehlt oder fehlerhaft ist, verweigert der Server die Auslieferung der Daten.Das ist genau das, was man sich von einer sicheren Umgebung wünscht.
Dieselbe Strategie lässt sich nicht nur auf die Authentifizierung, sondern auch auf die Autorisierung ausweiten. Das heißt, ausgehend von req.user Sie können bestimmte Rollen oder Berechtigungen überprüfen, bevor die Routenlogik ausgeführt wird, sodass Benutzer ohne ausreichende Berechtigungen ausgeschlossen werden, selbst wenn sie über ein gültiges Token verfügen.
In komplexeren Projekten ist der Einsatz von Bibliotheken wie Passport und dessen JWT-Strategie üblich. Passport bietet hochflexible Middleware mit Hunderten von Strategien für verschiedene Anmeldemethoden ( Google , Facebook , SAML usw.). Im Fall von JWT ermöglicht es die zentrale Konfiguration des Token-Eingangs und der Validierung und vereinfacht so den Schutz mehrerer Endpunkte mit nur einer Konfigurationszeile pro Route.
Sicherheitsbest Practices für JWT in Node.js
Die Implementierung von JWT ist relativ einfach, erfordert aber für eine sichere Anwendung die Einhaltung einiger bewährter Sicherheitspraktiken . Die erste und unabdingbare ist die Verwendung von HTTPS für den gesamten Datenverkehr . Base64 verschlüsselt keine Daten. Wenn Sie also ohne TLS-Verschlüsselung arbeiten, könnte jeder das Token abfangen und gegen Sie verwenden.
Es ist außerdem unerlässlich, die Gültigkeitsdauer von Zugriffstoken zu kontrollieren . Zugriffstoken sollten eine relativ kurze Lebensdauer haben (z. B. Minuten oder Stunden), um den Schaden im Falle eines Diebstahls zu minimieren. Werden gültige Token tagelang oder wochenlang nicht erneuert, vervielfacht sich das Risiko in jedem Kompromittierungsszenario.
Der Speicherort des JWT auf dem Client ist entscheidend. Eine Möglichkeit besteht darin, es in Cookies mit dem httpOnly-Flag zu speichern und, wenn möglich, eine sichere Umgebung zu schaffen, um das Risiko von XSS-Angriffen zu minimieren. Alternativ kann es im Arbeitsspeicher oder im lokalen Speicher abgelegt werden . In diesem Fall ist jedoch besondere Vorsicht beim clientseitigen Code und allen verwendeten Bibliotheken geboten.
Serverseitig müssen die zum Signieren von Token verwendeten Schlüssel mithilfe von Geheimnismanagern (KMS, Vault, Managed Services auf AWS oder Azure usw.) ordnungsgemäß geschützt werden. Es ist nicht ratsam, Schlüssel ungeschützt im Klartext in Repositories oder in auf den Server hochgeladenen Dateien zu speichern.
Abschließend empfiehlt es sich, nur die unbedingt notwendigen Informationen in die Nutzdaten aufzunehmen. Hochsensible Daten (wie Kreditkartennummern oder medizinische Informationen) sollten nicht im JWT enthalten sein , auch nicht verschlüsselt, es sei denn, es gibt sehr spezifische Anforderungen und zusätzliche Maßnahmen wie ein korrekt konfiguriertes JWE.
Refresh-Tokens: Lange Sitzungen ohne Sicherheitsverlust
Die Verwendung kurzlebiger JWTs verbessert die Sicherheit erheblich, kann aber für den Benutzer lästig sein, wenn er sich ständig neu anmelden muss. Hier kommen Refresh-Token ins Spiel : Sie ermöglichen die Erneuerung von Zugriffstoken, ohne die ursprünglichen Anmeldeinformationen erneut senden und ohne herkömmliche Serversitzungen aufrechtzuerhalten.
Das Schema ist recht einfach: Nach erfolgreicher Authentifizierung gibt der Server zurück zwei Token: ein kurzlebiges JWT-Zugriffstoken und ein längerlebiges Aktualisierungstoken. Der Client verwendet das erstere, um die API aufzurufen, und wenn es abläuft, ruft einen bestimmten Endpunkt auf (zum Beispiel /token) Senden des Aktualisierungstokens um ein neues Zugriffstoken zu erhalten.
Im Backend wird das Refresh-Token üblicherweise in einer persistenten Datenbank (z. B. Redis) zusammen mit Benutzerinformationen, Erstellungs- und Ablaufdatum sowie einem Status (aktiv oder widerrufen) gespeichert. Es wird nicht empfohlen, das Refresh-Token vollständig isoliert zu speichern , da es dann nicht zentral ungültig gemacht werden kann.
Bei einer Anfrage zur Passwortverlängerung prüft der Server, ob das Aktualisierungstoken existiert, dem richtigen Benutzer zugeordnet ist, nicht auf einer Sperrliste steht und nicht abgelaufen ist. Ist alles in Ordnung, generiert er ein neues JWT mit den Benutzerdaten und sendet es zurück. So kann der Benutzer weiterarbeiten, ohne sein Passwort erneut eingeben zu müssen.
Wichtig ist, dass ein Mechanismus zum Widerrufen oder Deaktivieren von Aktualisierungstoken vorhanden sein sollte . Verliert ein Benutzer beispielsweise ein Gerät oder besteht der Verdacht auf eine Datenschutzverletzung, sollte ein Administrator (oder der Benutzer selbst, je nach Design) das mit diesem Gerät verknüpfte Aktualisierungstoken ungültig machen können, um den Zugriff zu unterbrechen, ohne alle anderen Benutzer abmelden zu müssen.
Dieser Ansatz ist besonders nützlich, wenn dieselbe Identität von mehreren Geräten verwendet wird. Wird eines dieser Geräte kompromittiert, kann lediglich dessen Aktualisierungstoken ungültig gemacht werden, während die übrigen Geräte weiterhin normal funktionieren. In Kombination mit kurzlebigen Zugriffstoken wird das Zeitfenster, in dem ein Angreifer ein gestohlenes Token missbrauchen kann, deutlich reduziert.
Erweitertes Design mit JWT: Rollen, Microservices und Schlüsselverwaltung
Über das einfache Beispiel von Logins und geschützten Routen hinaus stellen reale Projekte zusätzliche Herausforderungen dar. Eine der ersten Erweiterungen besteht darin, Rollen und Berechtigungen in die Token-Payload aufzunehmen , sodass das Backend entscheiden kann, ob ein Benutzer auf eine bestimmte Ressource zugreifen oder eine bestimmte Aktion ausführen darf, ohne bei jeder Anfrage die Datenbank abfragen zu müssen.
Dies ist besonders in Microservice-Architekturen von Vorteil, da verschiedene Dienste dasselbe JWT verifizieren können, ohne Sitzungen teilen zu müssen. In diesem Kontext können Sie zwischen symmetrischen (gemeinsames Geheimnis) und asymmetrischen (öffentliches/privates Schlüsselpaar) Signaturen wählen und den öffentlichen Schlüssel über JWK veröffentlichen, sodass andere Dienste die Signatur verifizieren können, ohne das Ausstellungsgeheimnis zu kennen.
Die Schlüsselverwaltung wird entscheidend, wenn mehrere Aussteller, Drittanbieterdienste oder die Integration mit Identitätsanbietern (IdPs) im Spiel sind. Hier kommen Standards wie OpenID Connect und Dienste zur Ermittlung öffentlicher Schlüssel zum Einsatz , die es ermöglichen, Schlüssel zu rotieren, ohne den Betrieb von Clientanwendungen zu beeinträchtigen.
Es ist außerdem üblich, Token- Rotationsstrategien und Token-Versionen pro Benutzer zu definieren . Beispielsweise wird in der Benutzerdatenbank ein Feld namens „Version“ geführt, das in der JWT-Nutzlast enthalten ist. Ändert sich diese Version (etwa durch eine globale Abmeldung, eine Passwortänderung oder einen Sicherheitsvorfall), werden alle vorherigen Token als ungültig betrachtet, selbst wenn sie noch nicht abgelaufen sind.
In professionellen Umgebungen wird die JWT-Authentifizierungsarchitektur typischerweise durch Auditing, Ratenbegrenzungen für Anmelde- und Verlängerungs-Endpunkte, Überwachung fehlgeschlagener Versuche und Warnmeldungen bei ungewöhnlichen Mustern ergänzt. All dies unterstützt die Verwendung von JWT, ersetzt es aber nicht und trägt zum Aufbau eines wirklich robusten Systems bei.
Schließlich ist es, wenn das Backend Teil einer größeren Plattform ist (zum Beispiel mit SPA-Frontends, mobilen Apps , Power BI-Analyse-Dashboards oder der Integration mit KI- Agenten ), von entscheidender Bedeutung, die Authentifizierung von Anfang an unter Berücksichtigung dieser Szenarien zu konzipieren, um sicherzustellen, dass Token von verschiedenen Komponenten kontrolliert verwendet werden können, ohne unnötige Türen zu öffnen.
Die Authentifizierung mit JWT in einer Node.js-API, die kurzlebige Zugriffstoken, sichere Aktualisierungstoken, Middleware zum Schutz des Routenverlaufs und eine robuste Schlüssel- und Prüfrichtlinie kombiniert, ermöglicht die Entwicklung skalierbarer, effizienter und benutzerfreundlicher Systeme . Durch die sorgfältige Auswahl der in die Nutzdaten aufzunehmenden Daten, die Implementierung von HTTPS, den Schutz Ihrer Geheimnisse und die frühzeitige Planung von Token-Widerruf und -Erneuerung schaffen Sie eine solide Grundlage für die Entwicklung von Funktionen wie erweiterten Berechtigungen, Drittanbieterintegrationen oder Cloud-Bereitstellungen – ohne unangenehme Überraschungen.
Leidenschaftlicher Autor über die Welt der Bytes und der Technologie im Allgemeinen. Ich liebe es, mein Wissen durch Schreiben zu teilen, und genau das werde ich in diesem Blog tun und Ihnen die interessantesten Dinge über Gadgets, Software, Hardware, technologische Trends und mehr zeigen. Mein Ziel ist es, Ihnen dabei zu helfen, sich auf einfache und unterhaltsame Weise in der digitalen Welt zurechtzufinden.