Zum Inhalt springen

JWT-Schwachstellen

CWE-347OWASP A07:2021Aktualisiert 4. September 20266 Min. Lesezeit

JWT-Schwachstellen sind Fehler beim Signieren oder Überprüfen eines JSON Web Token. Wer sie findet, fälscht ein Token und wird zu einem beliebigen Nutzer. Die häufigen Fälle: ein akzeptiertes alg:none, die Verwechslung von HS256 und RS256, eine übersprungene Signaturprüfung und ignorierte Claims wie exp, aud und iss. Was hilft: ein fest vorgegebener Algorithmus, die Prüfung jedes Claims und eine kurze Gültigkeit.

Ein JSON Web Token reist bei jeder Anfrage zwischen Browser und Schnittstelle mit, und Ihr Server vertraut allem, was er darin liest: wer der Nutzer ist, welche Rolle er hat, wann die Sitzung endet. Dieses Vertrauen trägt nur, solange die Signatur korrekt geprüft wird. Wird sie es nicht, schreibt ein Angreifer das Token um und wird zu wem er möchte. Dieser Artikel geht die üblichen JWT-Fehler durch und zeigt, wie Sie sie schließen.

Was sind JWT-Schwachstellen?

JWT-Schwachstellen sind Fehler darin, wie eine Anwendung ein JSON Web Token (JWT) ausgibt oder überprüft. Ein JWT ist ein kompakter, signierter Nachweis über die Identität eines Nutzers und seine Eigenschaften, den der Server bei der Anmeldung ausstellt und bei jeder folgenden Anfrage als gültig ansieht. Ein Token besteht aus drei durch Punkte getrennten Teilen: einem Header mit dem Signaturalgorithmus, den Nutzdaten mit den Claims und einer Signatur über die ersten beiden Teile. Die Signatur ist das Einzige, was das Token an Ihren Server bindet. Wer ihre Prüfung aushebelt, macht die Claims editierbar.

Stellen Sie sich ein JWT als Festivalbändchen vor. Farbe und Hologramm belegen, dass ein Ordner es ausgegeben hat, weshalb Sie am Eingang nicht erneut Ihren Ausweis zeigen. Das trägt nur, solange der Ordner das Hologramm tatsächlich ansieht. Eine JWT-Schwachstelle ist der Ordner, der nur auf die Farbe blickt und alle durchwinkt, oder der ein Bändchen annimmt, das ein Besucher selbst angemalt hat.

Die Fehler fallen in eine überschaubare Zahl von Mustern: ein akzeptierter Algorithmus none, die Verwechslung eines symmetrischen mit einem asymmetrischen Verfahren, das Dekodieren eines Tokens ganz ohne Signaturprüfung und eine geprüfte Signatur bei gleichzeitig ignorierten Standard-Claims wie Ablaufzeit, Zielgruppe und Aussteller. Ein eigener Fehler ist es, die Nutzdaten für vertraulich zu halten und Geheimnisse darin abzulegen.

Wie funktioniert ein JWT-Angriff?

Nehmen Sie eine Schnittstelle, die den aktuellen Nutzer bei jeder Anfrage unmittelbar aus dem Token liest. Der Entwickler prüft die Signatur, überlässt die Einzelheiten aber der Bibliothek.

Verwundbar:

const jwt = require("jsonwebtoken");
const fs = require("node:fs");
const oeffentlicherSchluessel = fs.readFileSync("public.pem");

// Verwundbar: der Algorithmus stammt aus dem Token-Header,
// und weder Aussteller noch Zielgruppe noch Ablauf werden erzwungen.
function aktuellerNutzer(token) {
  return jwt.verify(token, oeffentlicherSchluessel);
}

Weil die zulässigen algorithms nicht vorgegeben sind, vertraut die Prüfung dem, was der Header behauptet. Aus dieser einen Auslassung folgen zwei Fälschungen.

Die erste ist der alg:none-Angriff. Ein Angreifer setzt den Algorithmus im Header auf none, ändert die Nutzdaten so, dass sie ihm eine Administratorrolle geben, und sendet eine leere Signatur. Eine Prüfung, die none akzeptiert, überspringt den Signaturvergleich und liefert die gefälschten Claims zurück. Auf der Leitung sieht die Anfrage so aus, wobei der abschließende Punkt die leere Signatur markiert:

GET /konto HTTP/1.1
Host: api.beispiel.de
Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiIxMDAxIiwicm9sZSI6ImFkbWluIn0.

Dekodiert lautet der Header {"alg":"none"} und die Nutzdaten lauten {"sub":"1001","role":"admin"}. Nichts hat das signiert, und nichts musste es.

Die zweite ist die Verwechslung von HS256 und RS256. Ihr Server signiert mit einem privaten RSA-Schlüssel und prüft mit dem passenden öffentlichen, der von Natur aus nicht geheim ist. Ein Angreifer nimmt diesen öffentlichen Schlüssel, signiert ein Token mit HMAC-SHA256 und verwendet dabei die Bytes des öffentlichen Schlüssels als gemeinsames Geheimnis, und setzt den Header auf HS256. Eine Prüfung, die den Algorithmus dem Header entnimmt, rechnet HMAC mit dem öffentlichen Schlüssel als Geheimnis, also mit genau dem Wert, den der Angreifer verwendet hat, und die Signatur passt. Das asymmetrische Verfahren fällt damit zu einem symmetrischen zusammen, dessen Schlüssel öffentlich ist.

Der einfachste Fehler ist noch schlichter: dekodieren ohne jede Prüfung. Ein Aufruf wie jwt.decode(token) liest die Claims und rührt die Signatur nie an, sodass jedes selbst getippte Token angenommen wird.

Die Abhilfe gibt einen Algorithmus fest vor und prüft die Claims, die das Token mitbringt.

Sicher:

const jwt = require("jsonwebtoken");
const fs = require("node:fs");
const oeffentlicherSchluessel = fs.readFileSync("public.pem");

// Sicher: ein Algorithmus ist fest vorgegeben, die Standard-Claims werden erzwungen.
function aktuellerNutzer(token) {
  return jwt.verify(token, oeffentlicherSchluessel, {
    algorithms: ["RS256"],
    issuer: "https://auth.beispiel.de",
    audience: "https://api.beispiel.de",
    maxAge: "15m",
  });
}

Die Festlegung von algorithms auf ["RS256"] weist sowohl none als auch jede HS256-Fälschung ab, denn HMAC ist kein Verfahren mehr, das die Prüfung überhaupt ausführt. Die Optionen issuer und audience binden das Token an Ihren Dienst, sodass ein gültiges, aber für eine andere Anwendung ausgestelltes Token abgelehnt wird. Die Option maxAge sorgt zusammen mit dem Claim exp dafür, dass ein gestohlenes Token nur kurz nützlich bleibt.

Ein signiertes JWT ist nicht verschlüsselt. Header und Nutzdaten sind lediglich base64url-kodiert, was bedeutet, dass jeder, der das Token besitzt, sie lesen kann. Legen Sie nie Passwörter, API-Schlüssel oder personenbezogene Daten hinein, die Sie nicht auf eine Postkarte schreiben würden; muss der Inhalt geheim bleiben, verwenden Sie ein verschlüsseltes Token (JWE).

Welche Auswirkungen haben JWT-Schwachstellen?

Ein JWT ist ein Nachweis, dem die gesamte Schnittstelle vertraut, weshalb ein einziger Prüffehler eine Umgehung der Authentifizierung an allen Endpunkten zugleich bedeutet. Wer ein Token fälscht, setzt beliebige Werte für sub und role: Er liest und ändert die Daten anderer Nutzer und steigt vom gewöhnlichen Konto zum Administrator auf. Trifft die Anwendung anhand des Tokens auch Autorisierungsentscheidungen, ist das eine Broken Access Control, die durch die Vordertür geliefert wird.

Die geschäftliche Auswirkung reicht von hoch bis kritisch. Die vollständige Übernahme von Konten, der Abfluss regulierter personenbezogener Daten mit Meldepflicht nach der DSGVO und das Verfälschen von Datensätzen stehen am oberen Ende. Enthalten die Nutzdaten zusätzlich Geheimnisse, kommt eine Information Disclosure hinzu, denn das Token war die ganze Zeit lesbar. Ein signiertes JWT lässt sich nach der Ausgabe nicht von sich aus widerrufen, sodass ein gefälschtes oder gestohlenes Token bis zum Ablauf gültig bleibt; genau deshalb zählt eine kurze Gültigkeit.

Wie spürt man JWT-Schwachstellen auf?

Beginnen Sie damit, ein echtes Token zu dekodieren: An den Punkten trennen und die ersten beiden Teile base64url-dekodieren. Lesen Sie den Algorithmus im Header und die Claims in den Nutzdaten und halten Sie fest, ob exp, aud und iss vorhanden sind und tatsächlich erzwungen werden. Ein Token ohne Ablauf oder eines, das für eine andere Zielgruppe ausgestellt und hier dennoch angenommen wird, ist bereits ein Befund.

Danach wird die prüfende Stelle selbst getestet. Setzen Sie den Algorithmus im Header auf none und entfernen Sie die Signatur. Signieren Sie ein RS256-Token mit HS256 neu und verwenden Sie dabei den öffentlichen Schlüssel als Geheimnis. Verändern Sie einen einzelnen Claim und sehen Sie nach, ob die Änderung angenommen wird. Entfernen oder datieren Sie exp zurück und spielen Sie ein altes Token erneut ein. Werkzeuge wie die JWT-Editor-Erweiterung der Burp Suite oder das quelloffene jwt_tool automatisieren jede dieser Proben.

Scanner melden die offensichtlichen Fälle, die interessanten liegen jedoch hinter der Anmeldung und hängen davon ab, wie jeder Endpunkt das Token liest. AssistSec untersucht Ausgabe und Prüfung von JWTs im Rahmen eines Penetrationstests und zeigt je Befund das genaue gefälschte Token, das die Anwendung angenommen hat.

Wie verhindert man JWT-Schwachstellen?

  • Geben Sie den Algorithmus fest vor. Übergeben Sie der Prüfung eine ausdrückliche Liste mit genau einem Verfahren, etwa algorithms: ["RS256"], und lassen Sie weder none zu noch die Wahl durch das Token.
  • Prüfen Sie jeden Standard-Claim. Erzwingen Sie exp und nbf für die Zeitfenster und prüfen Sie aud und iss, damit ein anderswo ausgestelltes Token nicht bei Ihnen eingespielt werden kann.
  • Halten Sie die Gültigkeit kurz. Geben Sie Zugriffstoken Minuten, keine Tage, und verwenden Sie für längere Sitzungen ein eigenes Erneuerungstoken, damit ein abgeflossenes Token schnell verfällt.
  • Schützen und trennen Sie die Schlüssel. Verwenden Sie für HS256 ein langes, zufälliges Geheimnis, halten Sie private Schlüssel aus dem Quellcodeverzeichnis heraus und rotieren Sie mit einem veröffentlichten kid, damit sich ein kompromittierter Schlüssel zurückziehen lässt.
  • Legen Sie nie Geheimnisse in die Nutzdaten. Behandeln Sie das Token als öffentliche Daten und greifen Sie zu einem verschlüsselten Token (JWE), wenn der Inhalt wirklich verborgen bleiben muss.
  • Nutzen Sie eine gepflegte Bibliothek und halten Sie sie aktuell. Verlassen Sie sich auf eine geprüfte Umsetzung statt auf selbst gebautes Parsen, und aktualisieren Sie sie, denn ältere Fassungen verbreiteter Bibliotheken akzeptierten none standardmäßig.

Quellen

Häufige Fragen

Ist ein JWT verschlüsselt?

Nein, standardmäßig nicht. Ein gewöhnliches signiertes JWT ist lediglich base64url-kodiert, sodass jeder, der das Token besitzt, Header und Nutzdaten lesen kann. Die Signatur belegt, dass das Token nicht verändert wurde, sie verbirgt den Inhalt jedoch nicht. Müssen die Nutzdaten geheim bleiben, verwenden Sie ein verschlüsseltes Token (JWE) und legen Sie nie Passwörter oder Schlüssel in ein einfaches signiertes JWT.

Was ist der alg:none-Angriff?

Im Header eines JWT steht der Algorithmus der Signatur. Akzeptiert die prüfende Stelle den Wert none, überspringt sie die Signaturprüfung vollständig. Ein Angreifer setzt den Header auf alg:none, ändert die Nutzdaten nach Belieben, sendet eine leere Signatur und wird als das akzeptiert, was die Nutzdaten behaupten. Die Abhilfe besteht darin, den zulässigen Algorithmus fest vorzugeben und none nie zuzulassen.

Wie funktioniert die Verwechslung von HS256 und RS256?

Bei RS256 signiert der Server mit einem privaten Schlüssel und prüft mit dem öffentlichen, der nicht geheim ist. Liest die prüfende Stelle den Algorithmus aus dem Token, statt ihn vorzugeben, signiert ein Angreifer ein Token mit HS256 und verwendet den öffentlichen Schlüssel als HMAC-Geheimnis. Die Prüfung rechnet dann HMAC mit genau diesem öffentlichen Schlüssel, und die gefälschte Signatur passt. Ein fest vorgegebenes RS256 verhindert das.

Wie lange sollte ein JWT gültig sein?

Halten Sie Zugriffstoken kurz, in der Größenordnung weniger Minuten bis etwa einer Viertelstunde, und geben Sie für längere Sitzungen ein eigenes Erneuerungstoken aus. Eine kurze Gültigkeit begrenzt, wie lange ein gestohlenes oder gefälschtes Token nützlich bleibt, denn ein signiertes JWT lässt sich nach der Ausgabe nicht von sich aus widerrufen. Erzwingen Sie den Claim exp bei jeder Prüfung.

Verwandte Artikel

Zum Suchen / drücken · Esc