Lerne Coding
Was sind JSON Web Tokens? Wie funktioniert das Konzept?

Was sind JSON Web Tokens?

Inhaltsverzeichnis
[[TABLE OF CONTENTS]]
access_timeGeschätzte Lesezeit ca. Minuten

Ein JWT oder auch JSON Web Token ist zur Authentifizierung & Autorisierung gedacht, er kann sowohl von Clients und Servern gelesen werden. Er kann verwendet werden, um Daten sicher zwischen verschiedenen Anwendungen auszutauschen, durch die Signatur kann der JWT immer wieder mithilfe von JWKS verifiziert werden, dass eine Manipulation vom Inhalt ausgeschlossen werden kann.

Eine Definition des JWT Standards kann im RFC 7519 genauer nachgelesen werden, auf die wichtigsten Details werden wir im Abschnitt über den Aufbau von einem JWT betrachten.

Wozu sind JSON Web Tokens da?

JSON Web Tokens sind vorwiegend dann interessant, wenn es sich um eine Anwendung mit mehreren Microservices handelt, da dort der Nutzer häufig in verschiedene Systemen authentifiziert und autorisiert werden muss, um auf diese zugreifen zu dürfen. Ein Vorteil hier gegenüber einer Session ist, da der JWT eine Signatur zur Verifizierung enthält und eine Payload mit Informationen über den Nutzer. Es muss keine Kommunikation über einen Authentifizierungs-Server stattfinden bei jeder Anfrage, erst wenn der JWT abgelaufen ist, muss dieser wieder erneuert werden. Dadurch, dass die Payload zusätzliche Informationen über den Nutzer enthalten kann, muss nicht für jede Anfrage wieder eine Datenbank abgefragt werden, um Informationen über den Nutzer zu erhalten, diese können ganz bequeme bei der Erstellung im JWT gespeichert werden.

Der Vorteil von JSON Web Tokens ist auch gleichzeitig deren Nachteil, den man nicht außer Acht lassen sollte, dadurch das die Informationen im JWT gespeichert werden ist ein JWT ohne entsprechende zusätzliche Angaben unbegrenzt gültig. Es kann vorkommen, dass sich die Informationen eines Benutzers in der Datenbank ändern, während das zugehörige JWT-Token noch seine Gültigkeit behält – da der JWT nur über eine zeitliche Limitierung seine Gültigkeit erhält. Diese kann leicht zu unerwünschten Nebeneffekten führen, zum Beispiel, dass ein Nutzer von den Berechtigungen herabgestuft wird, aber sein alter JWT noch weiter verwendet werden kann, bis dieser zeitlich abläuft. Manche Systeme haben, um diese Problematik zu lösen, eine Sperrliste, wo solche JWT’s draufgeschrieben werden, die vorzeitig ihre Gültigkeit verlieren sollen, alle Microservices, die dann mit dem JWT arbeiten sollen, müssen diese noch einmal gegen validieren lassen. Dadurch verliert der JWT seine Unabhängigkeit von Dritten, um zu prüfen, ob der JWT noch verwendet werden darf.

Ein großer Einsatzbereich, wo JWT’s eine hervorragende Lösung sind, sind unter anderem die Single-Sign-Ons (SSO). Bei einem solchen Konzept ist im JWT etwa eine User-ID gespeichert, die von allen Anwendung verwendet wird, jede Anwendung hat noch mal eine eigene Datenbank, um anwendungsspezifische Informationen des Nutzers zu speichern. Im JWT könnte zum Beispiel auch als Information gespeichert sein, auf welche Anwendungen welcher Nutzer zugreifen darf. Diese geht, weil über die Signatur des JWT’s sichergestellt werden kann, dass der Inhalt nicht manipuliert wurde. Für diesen Zweck gibt es dann das sogenannte JSON Web Key Set (JWKS) diese ist ein JSON Dokument mit öffentlichen Schlüsseln, um die Signatur zu überprüfen, die mithilfe des privaten Schlüssels erstellt wurde.

Wie sind JSON Web Tokens aufgebaut?

Ein JSON Web Token besteht reguläre aus drei Teilen, oder in seltenen Fällen auch mal aus nur zwei Teilen. Ein JWT kann man an der Aufteilung erkennen HEADER.PAYLOAD.SIGNATUR – wobei der Header und die Payload Base64 encodiert werden.

Zum Beispiel könnte ein JWT Token wie folgt aussehen, wenn du ihn in der Webseite jwt.io einfügst, kannst du den Inhalt lesen, den reguläre ist die Payload lesbar. Alternative kann auch noch eine Verschlüsselung mittels JSON Web Encryption eingesetzt werden.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkhlbGxvQ29kaW5nIiwiaWF0IjoxNTE2MjM5MDIyfQ.E7GPR9oycyTiXpnghy2Pxn43lUTgLjTUy_BMEYq4In4

Wie funktioniert der JOSE Header?

Der Header wird auch als JOSE (JSON Object Signing and Encryption) Header bezeichnet, den dieser enthält ausschließlich Informationen über die Signierung & Verschlüsselung.

Ein typischer Aufbau enthält, eine Angabe über den Algorithmus, mit dem die Signatur erstellt wurde und eine Definition, dass es sich um ein JWT handelt. Hierbei ist wichtig, dass, die Typangabe optional ist, aber gerne regulär auf JWT gesetzt wird. Diese ist relevant für Anwendungen, die auch andere, Typen wie einen Access Token akzeptieren, dieser aber in einer ähnlichen Datenstruktur auftritt, um die verschiedenen Typen dann einfach unterscheiden zu können.

{
  "alg": "HS256",
  "typ": "JWT"
}

Es können verschieden Arten von Algorithmen verwendet werden, der HS256 (HMAC SHA-256) ist eine symmetrische Technik, wobei Sender und Empfänger beide den gleichen geheimen Schlüssel benötigen. RSA (Rivest-Shamir-Adleman) hingegen, ist eine Technik, wobei es einen öffentlichen und einen privaten Schlüssel gibt, der private Schlüssel wird verwendet, um die Signatur zu erstellen. Und der Öffentliche Schlüssel kann verwendet werden, um die Signatur vom JWT zum Beispiel wieder zu validieren als gültig.

Alternative zu den Algorithmen wie HS256 & RS256 kann auch „none“ als validiert Wert eingesetzt werden, in diesem Fall entfällt die Signatur.

Wie funktioniert die Signatur (JWS) ?

Die Signatur oder auch JSON Web Signatur (JWS) genannt, definiert in RFC 7515. Grundsätzlich enthält der 3te Teil des JSON Web Tokens die Signatur.

  • In dem Fall, dass kein Algorithmus definiert wurde, entfällt die Signatur. Allerdings gibt es eher selten Fälle, in denen diese Verhalten erwünscht ist, diese ist meistens ein Verhalten für die Entwicklung, um schnell zu prüfen, wie eine Anwendung mit bestimmten Werten umgeht.
  • Das zweite Verfahren haben wir bereits beschrieben, dabei Arbeit man mit einem Algorithmus wie HS256 oder RS256 um einen eindeutigen Wert basierend auf den Daten zu erstellen. Um Manipulationen der Payload ausschließen zu können.
  • Das dritte Verfahren geht noch einen Schritt weiter mittels einer Verschlüsselung (JWE) dort wird die Payload entsprechend noch einmal mit einem privaten Schlüssel verschlüsselt, und muss entsprechend erst entschlüsselt werden, um wieder im Klartext lesebar zu sein.

Die Details zu JSON Web Encryption (JWE) würden hier etwas zu weit gehen, weshalb ich dir den Artikel von Scott Brady auf Englisch empfehlen will: https://www.scottbrady91.com/jose/json-web-encryption

Was beinhaltet die Payload?

Die Payload beinhaltet unterschiedlichste Informationen über den Nutzer, genauer gesagt die übermittelte Entität. Typischerweise werden die Keys im Payload Objekt nicht als Key bezeichnet, sondern als sogenannte Claims. Es gibt 3 verschiedene Arten von Claims:

  • Registrierte Claims
    • Registrierte Claims sind solche, die fest definiert sind, bei der IANA. Meist sind diese sehr kurzen Namen wie exp für Expiration Time. Oder iss für den Herausgeber, meistens wird dort die URL des Authentifizierungs-Servers mitgegeben. Der Grund für die kurzen Namen ist, den JWT möglichst klein zuhalten.
  • Öffentliche Claims
    • Öffentliche Claims sind solche, die allgemeine Informationen enthalten, die für alle Services relevant sind, die mit dem JWT arbeiten werden.
  • Private Claims
    • Die privaten Claims sind für Informationen, die nicht für alle Services relevant sind, das können etwa spezifische Berechtigungen für Teilbereiche der Anwendung sein. Häufig wird für diese Bereiche als Claim Name ein Namespace gemacht über die jeweilige URL, um Kollisionen zu vermeiden.

Eine Payload mit den verschiedenen Claim Typen könnte zum Beispiel folgendermaßen aussehen. Wir haben drei registrierte Claims für die sub, das Subjekt, die Entität, in unserem Fall eine User-ID, den Herausgeber (iss) des JSON Web Token. Und die Zeit, wann der Token abläuft, exp.

{
    "sub": "user-id",
    "iss": "https://hellocoding.de",
    "exp": 169999999,
    "name": "Felix",
    "email": "office@hellocoding.de",
    "https://hellocoding.de/game":{
        "level": 10
    }
}

Zusätzlich haben wir jetzt noch zwei öffentliche Claims, um zu definieren, um welchen Nutzer es sich handelt, diese geschieht zwar auch über das Subject, aber so könnten wir in unsere Anwendung jetzt direkt den Nutzer begrüßen, ohne einen Server abfragen zu müssen.

Abschließend gibt es noch einen privaten Claim für ein Game, dort wird das aktuelle Level zum Beispiel drinnen gespeichert. Hier ist die Besonderheit, dass wir die URL verwenden, um eine Kollision mit anderen Claims ausschließen zu können.

Was gehört nicht in die Payload?

Es ist wichtig zu beachten, es sollten niemals sensible Informationen wie Passwörter, Bankverbindungen in die Payload, auch wenn diese theoretisch möglich ist. Das Risiko von einem Hijacking kann niemals komplett ausgeschlossen werden. Auch unter einer Verschlüsselung mittels JWE kann ein Schlüssel mal an die Öffentlichkeit gelangen und schon können von externen die Daten gelesen werden.

Auch mit einer SSL Verbindung gibt es noch Sicherheitslücken für Hijacking, es ist etwa typisch, dass JWT's im Local Storage gespeichert werden. Das bedeutet das auch andere externe Script zum Beispiel auf diese zugreifen können, da der Local Storage nicht wie Cookies zusätzlich abgesichert werden kann, das diese zum Beispiel nur vom Server gelesen werden können.

Letztlich sollten sehr sensible Informationen wie Passwörter, Bankverbindungen etc. am besten immer direkt vom jeweiligen Service aus der Datenbank gelesen werden, genauer gesagt von einer Schnittstelle, die nicht für den Client zugänglich ist.

Grundsätzlich falls du den Gedanken hast im JWT ein Passwort unterzubringen, oder es bereits in einer Session hattest, solltest du einmal über das Konzept deiner Umsetzung nachdenken. Es gibt bestimmt auch einen alternativen Weg, der ohne ein dauerhaft gespeichertes Passwort funktionieren könnte.

Session vs. JSON Web Token?

Es gibt drei Bereiche, über die wir sprechen müssen, wenn es darum geht, was der Unterschied von einer Session zu einem JSON Web Token ist.

  • Speicherort der Informationen
    • Bei einem JWT wird alles in der Payload gespeichert, was benötigt wird, um den Nutzer zu authentifizieren und zu autorisieren. Zusätzliche Informationen, die relevant sind für die jeweilige Anwendung zur Laufzeit.
    • Bei einer Session beinhaltet der Token keine Informationen, es ist eine zufällige Zeichenfolge, der, wie eine ID dem Server in der Regel in einem Cookie übergeben wird. Um dann die jeweiligen Daten aus einem Store zu laden und den Nutzer anhand dieser Informationen zu authentifizieren & autorisieren zu können.
  • Zustandslosigkeit
    • Die Daten der aktuellen Session werden auf dem Server zwischen den Anfragen gespeichert, um den Session-Token wieder dem richtigen Nutzer zuordnen zu können. Und weitere Informationen abfragen zu können.
    • Der JWT wird einmalig vom Server erstellt, danach hat der Server keine Information mehr über die Existenz des Tokens. Er wird jedes Mal neu vom Server auf die Signatur geprüft und die Payload wird jedes Mal aufs neue gelesen aus dem übermittelten JWT.
  • Skalierbarkeit & Unabhängigkeit
    • Die Session Daten werden auf dem Server gespeichert, das bedeutet, hat meine Anwendung mehrere Server muss ich dafür sorgen, dass die Sitzungsinformationen zwischen allen Server geteilt werden müssen. Zusätzlich müssen entsprechende Speicher bereitstehen, um alle Nutzer Sessions speichern zu können.
    • Bei den JWT’s können unabhängig viele Server mit diesen arbeiten, solange sie alle Zugriff auf den Public Key haben, zum Beispiel mittels JWKS. Das Speichern der Nutzerinformationen, erfordert keinen serverseitigen Speicherplatz, da diese im JWT beim Client gespeichert sind.

Wie kann ich JSON Web Tokens Implementieren in der Praxis?

Um einen JWT nun zu erstellen, benötigen wir die drei definierten Teile: JOSE Header, Payload, JWS Signatur. Als Beispielsprache für die Erstellung des JWT verwende ich Python, das Beispiel lässt sich allerdings auch auf jede andere Sprache adaptieren.

Das Beispiel ist simple gehalten mit einem HS256, mit einem RS256 wird es etwas komplexerer bezüglich der Umsetzung, allerdings hab ich hier darauf verzichtet, da es grundsätzlich in jeder Sprache bereits JWT Paket gibt, die dir die Arbeit vereinfachen werden. Der Vollständigkeit halber wollte ich allerdings nicht auf ein Beispiel verzichten!

import json
import base64
import hmac
import hashlib

header = {"alg": "HS256", "typ": "JWT"}  # unser JOSE Header

payload = {
    "sub": "user-id",
    "iss": "https://hellocoding.de",
    "exp": 169999999,
    "name": "Felix",
    "email": "office@hellocoding.de",
    "https://hellocoding.de/game": {"level": 10},
}  # unser payload

# dein geheimer schlüssel
secret_key = "my_secret_key"

# payload & header
encoded_header = (
    base64.urlsafe_b64encode(json.dumps(header).encode("utf-8"))
    .rstrip(b"=")
    .decode("utf-8")
)
encoded_payload = (
    base64.urlsafe_b64encode(json.dumps(payload).encode("utf-8"))
    .rstrip(b"=")
    .decode("utf-8")
)

payload_header = f"{encoded_header}.{encoded_payload}"

# erstellen der Signatur
signature = (
    base64.urlsafe_b64encode(
        hmac.new(
            secret_key.encode("utf-8"), payload_header.encode("utf-8"), hashlib.sha256
        ).digest()
    )
    .rstrip(b"=")
    .decode("utf-8")
)

print(f"{payload_header}.{signature}")  # der jwt token

Den erhalten JWT können wir zum Testen zum Beispiel bei jwt.io decodieren lassen und nach dem wir auch unsere Secret eingesetzt haben, erhalten wir die Ausgabe, dass unsere Signatur Valide ist.

Nützliche Pakete zur Implementierung

Nun sind wir so ziemlich am Ende angekommen, allerdings wirst du in den seltensten Fällen JWT komplett selbst implementieren wollen, sondern lieber auf eine Implementierung zurückgreifen.

Ich habe dir einmal meine persönlichen Empfehlungen für die verschiedensten Programmiersprachen herausgesucht.

Zusätzlich kann ich dir die Webseite https://jwt.io/libraries empfehlen, dort findest du eine Übersicht für viele andere Programmiersprachen wie Java, Golang, C++ und viel weitere.

Fazit

Es gibt extreme viele Möglichkeiten für den Einsatz von JSON Web Tokens, beachten sollte man aber auch, dass ein JWT nicht immer die Lösung sein muss. Eine Session Handling kann je nach Anwendung auch eine sinnvolle Lösung sein.

Hast du Anmerkungen zu diesem Artikel? Oder auch eigene Erfahrungen mit JSON Web Tokens gemacht, die sowohl Positiv als auch Negativ waren, hinterlasse uns gerne deine Meinung.

Kommentare zum Artikel

Es sind noch keine Kommentare vorhanden? Sei der/die Erste und verfasse einen Kommentar zum Artikel "Was sind JSON Web Tokens? Wie funktioniert das Konzept?"!

Kommentar schreiben

Verwante Beiträge
close