JSON Web Tokens (JWTs, ausgesprochen „Jots“) sind ein kompaktes und äußerst portables Mittel zum Austausch von Identitätsinformationen. Die JWT-Spezifikation war eine wichtige Grundlage von OpenID Connect und stellte ein Single Sign-On-Token für das OAuth 2.0-Ökosystem bereit. JWTs können auch als eigenständige Authentifizierungsdaten verwendet werden und stellen eine bessere Möglichkeit zur Zugriffskontrolle auf webbasierte APIs dar als herkömmliche API-Schlüssel.
NGINX Plus R10 und höher können JWTs direkt validieren. In diesem Blogbeitrag beschreiben wir, wie Sie NGINX Plus als API-Gateway verwenden, ein Front-End für einen API-Endpunkt bereitstellen und JWTs zur Authentifizierung von Clientanwendungen verwenden.
Native JWT-Unterstützung ist nur in NGINX Plus verfügbar, nicht in NGINX Open Source.
Redakteur – Dieser Blogbeitrag wurde im Dezember 2021 aktualisiert , um die in NGINX Plus R25 eingeführte Direktive auth_jwt_require
zu verwenden . Eine ausführliche Erläuterung der Direktive finden Sie unter „Benutzerdefinierte JWT-Validierungsregeln“ im Blog zur Ankündigung von NGINX Plus R25 .
NGINX Plus R15 und höher können auch den „Authorization Code Flow“ in OpenID Connect 1.0 steuern, was die Integration mit den meisten großen Identitätsanbietern ermöglicht. Weitere Einzelheiten finden Sie unter Ankündigung von NGINX Plus R15<.htmlspan> .
JWTs bestehen aus drei Teilen: einem Header, einer Nutzlast und einer Signatur. In der Übertragung sehen sie wie folgt aus. Wir haben Zeilenumbrüche zur besseren Lesbarkeit hinzugefügt (das eigentliche JWT ist eine einzelne Zeichenfolge) und eine Farbcodierung, um die drei Teile zu unterscheiden:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 . ewogICAgInN1YiI6ICJsYzEiLAogICAgImVtYWlsIjogImxpYW0uY3JpbGx5QG5naW54LmNvbSIsCn0= . VGYHWPterIaLjRi0LywgN3jnDUQbSsFptUw99g2slfc
Wie gezeigt trennt ein Punkt ( .
) Header, Nutzlast und Signatur. Header und Nutzlast sind Base64-codierte JSON-Objekte. Die Signatur wird mit dem im Alg
-Header angegebenen Algorithmus verschlüsselt, den wir sehen können, wenn wir unser Beispiel-JWT dekodieren:
Codiert | Entschlüsselt | |
---|---|---|
Kopfbereich | eyJhbGciOiJIUzI1NiIsInR5cCI6Ik |
{ |
Nutzlast | ewogICAgInN1YiI6ICJsYzEiLAogICAgImVtYWlsIjogImxpYW0uY3JpbGx5QG5naW54LmNvbSIsCn0= |
{ |
Der JWT-Standard definiert mehrere Signaturalgorithmen. Der Wert HS256
in unserem Beispiel bezieht sich auf HMAC SHA-256, den wir für alle Beispiel-JWTs in diesem Blogbeitrag verwenden. NGINX Plus unterstützt die im Standard definierten Signaturalgorithmen HS xxx
, RS xxx
und ES xxx
. Durch die Möglichkeit, JWTs kryptografisch zu signieren, eignen sie sich ideal als Authentifizierungsdaten.
Eine gängige Methode zur Authentifizierung eines API-Clients (des Remote-Softwareclients, der API-Ressourcen anfordert) ist ein gemeinsames Geheimnis, das im Allgemeinen als API-Schlüssel bezeichnet wird. Ein herkömmlicher API-Schlüssel ist im Wesentlichen ein langes und komplexes Passwort, das der Client bei jeder einzelnen Anfrage als zusätzlichen HTTP-Header sendet. Der API-Endpunkt gewährt Zugriff auf die angeforderte Ressource, wenn der angegebene API-Schlüssel in der Liste der gültigen Schlüssel enthalten ist. Im Allgemeinen validiert der API-Endpunkt die API-Schlüssel nicht selbst; stattdessen übernimmt ein API-Gateway den Authentifizierungsprozess und leitet jede Anfrage an den entsprechenden Endpunkt weiter. Neben der Entlastung von Rechenlasten bietet dies die Vorteile eines Reverse-Proxys, wie z. B. hohe Verfügbarkeit und Lastausgleich für eine Reihe von API-Endpunkten.
Es ist üblich, auf verschiedene API-Clients unterschiedliche Zugriffskontrollen und Richtlinien anzuwenden. Bei herkömmlichen API-Schlüsseln ist hierfür eine Suche erforderlich, um den API-Schlüssel mit einem Satz von Attributen abzugleichen. Das Durchführen dieser Suche bei jeder einzelnen Anfrage hat verständlicherweise Auswirkungen auf die Gesamtlatenz des Systems. Mit JWT sind diese Attribute eingebettet, sodass keine separate Suche erforderlich ist.
Die Verwendung von JWT als API-Schlüssel bietet eine leistungsstarke Alternative zu herkömmlichen API-Schlüsseln und kombiniert bewährte Authentifizierungstechnologie mit einem standardbasierten Schema zum Austausch von Identitätsattributen.
Die NGINX Plus-Konfiguration zum Validieren von JWTs ist sehr einfach.
Upstream API-Server {
Server 10.0.0.1;
Server 10.0.0.2;
}
Server {
Listen 80;
Standort /Produkte/ {
auth_jwt „Produkte-API“;
auth_jwt_key_file conf/api_secret.jwk;
Proxy-Pass http://API-Server;
}
}
Als Erstes geben wir im Upstream-
Block die Adressen der Server an, die den API-Endpunkt hosten. Der Standortblock
gibt an, dass alle Anforderungen an URLs, die mit /products/ beginnen, authentifiziert werden müssen. Die Direktive auth_jwt
definiert den Authentifizierungsbereich, der zurückgegeben wird (zusammen mit einem401
Statuscode), wenn die Authentifizierung fehlgeschlagen ist.
Die Direktive „auth_jwt_key_file“
teilt NGINX Plus mit, wie das Signaturelement des JWT validiert wird. In diesem Beispiel verwenden wir den HMAC SHA-256-Algorithmus zum Signieren von JWTs und müssen daher einen JSON-Webschlüssel in conf/api_secret.jwk erstellen, der den zum Signieren verwendeten symmetrischen Schlüssel enthält. Die Datei muss dem in der JSON Web Key-Spezifikation beschriebenen Format entsprechen. Unser Beispiel sieht folgendermaßen aus:
{"keys":
[{
"k":"ZmFudGFzdGljand0",
"kty":"oct",
"kid":"0001"
}]
}
Der symmetrische Schlüssel ist im Feld k
definiert und hier ist der Base64URL-codierte Wert der Klartext-Zeichenfolge fantasticjwt
. Wir haben den codierten Wert durch Ausführen dieses Befehls erhalten:
$ echo -n fantasticjwt | base64 | tr '+/' '-_' | tr -d '='
Das kty-
Feld definiert den Schlüsseltyp als symmetrischen Schlüssel (Oktettfolge). Schließlich definiert das Feld kid
(Key ID) eine Seriennummer für diesen JSON Web Key, hier0001
, wodurch wir mehrere Schlüssel in derselben Datei unterstützen (benannt durch die Direktive auth_jwt_key_file
) und den Lebenszyklus dieser Schlüssel und der mit ihnen signierten JWTs verwalten können.
Jetzt können wir JWTs an unsere API-Clients ausgeben.
Als Beispiel-API-Client verwenden wir eine „Angebotssystem“-Anwendung und erstellen ein JWT für den API-Client. Zuerst definieren wir den JWT-Header:
{
"typ":"JWT",
"alg":"HS256",
"kid":"0001"
}
Das Feld „Typ“
definiert den Typ als „JSON Web Token“, das Feld „Alg“
gibt an, dass das JWT mit dem HMAC SHA256-Algorithmus signiert ist, und das Feld „Kid“
gibt an, dass das JWT mit dem JSON Web Key mit dieser Seriennummer signiert ist.
Als nächstes definieren wir die JWT-Nutzlast:
{
"name":"Angebotssystem",
"sub":"quotes",
"iss":"Mein API-Gateway"
}
Das Sub
-Feld (Betreff) ist unsere eindeutige Kennung für den vollständigen Wert im Namensfeld
. Das Feld „iss“
beschreibt den Aussteller des JWT. Dies ist nützlich, wenn Ihr API-Gateway auch JWTs von Drittanbietern oder einem zentralen Identitätsverwaltungssystem akzeptiert.
Da wir nun alles haben, was wir zum Erstellen des JWT benötigen, befolgen wir diese Schritte, um es korrekt zu kodieren und zu signieren. Befehle und codierte Werte werden nur aus Gründen der Lesbarkeit in mehreren Zeilen angezeigt. Tatsächlich wird jeder Befehl als einzelne Zeile eingegeben oder erscheint in einer einzelnen Zeile.
Header und Nutzlast separat abflachen und Base64URL-kodieren.
$ echo -n '{"typ":"JWT","alg":"HS256","kid":"0001"}' | base64 | tr '+/' '-_' | tr -d '=' eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjAwMDEifQ $ echo -n '{"name":"Angebotssystem","sub":"Zitate","iss":"Mein API-Gateway"}' | base64 | tr '+/' '-_' | tr -d '=' eyJuYW1lIjoiUXVvdGF0aW9uIFN5c3RlbSIsInN1YiI6InF1b3RlcyIsImlzcyI6Ik 15IEFQSSBHYXRld2F5In0
Verketten Sie den codierten Header und die Nutzlast mit einem Punkt (.) und weisen Sie das Ergebnis der Variablen HEADER_PAYLOAD
zu.
$ HEADER_PAYLOAD=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjAwMDEifQ.eyJuYW1lIjoiUXVvdGF0aW9uIFN5c3RlbSIsInN1YiI6InF1b3RlcyIsIm lzcyI6Ik15IEFQSSBHYXRld2F5In0
Signieren Sie den Header und die Nutzlast mit unserem symmetrischen Schlüssel und kodieren Sie die Signatur mit Base64URL.
$ echo -n $HEADER_PAYLOAD | openssl dgst -binary -sha256 -hmac fantasticjwt | base64 | tr '+/' '-_' | tr -d '=' ggVOHYnVFB8GVPE-VOIo3jD71gTkLffAY0hQOGXPL2I
Hängen Sie die codierte Signatur an den Header und die Nutzlast an.
$ echo $HEADER_PAYLOAD.ggVOHYnVFB8GVPE-VOIo3jD71gTkLffAY0hQOGXPL2I > Zitate.jwt
Testen Sie, indem Sie eine authentifizierte Anfrage an das API-Gateway senden (in diesem Beispiel wird das Gateway auf dem lokalen Host ausgeführt).
$ curl -H "Autorisierung: Träger `cat quotes.jwt`" http://localhost/products/widget1
Der Curl
-Befehl in Schritt 5 sendet das JWT in Form eines Bearer Token an NGINX Plus, was NGINX Plus standardmäßig erwartet. NGINX Plus kann das JWT auch aus einem Cookie oder Abfragezeichenfolgenparameter abrufen. Um dies zu konfigurieren, fügen Sie der Direktive auth_jwt
den Parameter token=
hinzu. Mit der folgenden Konfiguration kann NGINX Plus beispielsweise das mit diesem Curl
-Befehl gesendete JWT validieren:
$ curl http://localhost/products/widget1?apijwt=`cat Zitate.jwt`
Server { abhören 80; Standort /Produkte/ { auth_jwt "Produkte-API" Token=$arg_apijwt ; auth_jwt_key_file conf/api_secret.jwk; Proxy-Passwort http://API-Server; } }
Nachdem Sie NGINX Plus konfiguriert und ein JWT wie oben gezeigt generiert und verifiziert haben, können Sie das JWT an den Entwickler des API-Clients senden und den Mechanismus vereinbaren, der zum Senden des JWT mit jeder API-Anfrage verwendet wird.
Einer der Hauptvorteile von JWTs als Authentifizierungsnachweise besteht darin, dass sie „Ansprüche“ übermitteln, die Entitäten darstellen, die mit dem JWT und seiner Nutzlast verknüpft sind (z. B. seinen Aussteller, den Benutzer, an den es ausgestellt wurde, und den beabsichtigten Empfänger). Nach der Validierung des JWT hat NGINX Plus Zugriff auf alle im Header und der Nutzlast vorhandenen Felder als Variablen. Der Zugriff erfolgt durch das Präfix $jwt_header_
oder $jwt_claim_
vor dem gewünschten Feld (zum Beispiel $jwt_claim_sub
für den Unteranspruch
). Dies bedeutet, dass wir die im JWT enthaltenen Informationen sehr einfach per Proxy an den API-Endpunkt weiterleiten können, ohne die JWT-Verarbeitung in der API selbst implementieren zu müssen.
Dieses Konfigurationsbeispiel zeigt einige der erweiterten Funktionen.
log_format jwt '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" "$http_user_agent" ' '$jwt_header_alg $jwt_claim_sub' ; limit_req_zone $jwt_claim_sub zone=10rps_per_client:1m Rate=10r/s; Server { abhören 80; Standort /products/ { auth_jwt "Produkte-API"; auth_jwt_key_file conf/api_secret.jwk; limit_req zone=10rps_per_client; Proxy-Pass http://API-Server; Proxy-Set_Header API-Client $jwt_claim_sub; Zugriffsprotokoll /var/log/nginx/access_jwt.log jwt; } }
Die Direktive log_format
definiert ein neues Format namens jwt
, das das allgemeine Protokollformat um zwei zusätzliche Felder erweitert, $jwt_header_alg
und $jwt_claim_sub
. Innerhalb des Standortblocks
verwenden wir die Direktive access_log
, um Protokolle mit den aus dem validierten JWT erhaltenen Werten zu schreiben.
In diesem Beispiel verwenden wir auch anspruchsbasierte Variablen, um die API-Ratenbegrenzung pro API-Client statt pro IP-Adresse bereitzustellen. Dies ist insbesondere dann nützlich, wenn mehrere API-Clients in ein einziges Portal eingebettet sind und nicht anhand der IP-Adresse unterschieden werden können. Die Direktive „limit_req_zone“
verwendet den JWT- Unteranspruch
als Schlüssel zum Berechnen von Ratenbegrenzungen, die dann durch Einbeziehung der Direktive „limit_req“
auf den Standortblock
angewendet werden.
Schließlich stellen wir das JWT-Thema als neuen HTTP-Header bereit, wenn die Anforderung an den API-Endpunkt weitergeleitet wird. Die Direktive proxy_set_header
fügt einen HTTP-Header namens API-Client
hinzu, den der API-Endpunkt problemlos nutzen kann. Daher muss der API-Endpunkt keine JWT-Verarbeitungslogik implementieren. Dies wird zunehmend wertvoller, wenn die Anzahl der API-Endpunkte zunimmt.
Von Zeit zu Zeit kann es erforderlich sein, das JWT eines API-Clients zu widerrufen oder neu auszustellen. Durch die Kombination eines einfachen Map-
Blocks mit der Direktive „auth_jwt_require“
können wir einem API-Client den Zugriff verweigern, indem wir sein JWT als ungültig markieren, bis das Ablaufdatum des JWT (dargestellt im „Exp“
-Anspruch) erreicht ist. An diesem Punkt kann der Map-
Eintrag für dieses JWT sicher entfernt werden.
In diesem Beispiel setzen wir die Variable $jwt_status
auf0
oder1
entsprechend dem Wert des Unteranspruchs
im Token (wie in der Variable „$jwt_claim_sub“
erfasst). Anschließend verwenden wir die Direktive „auth_jwt_require“
im Standortblock
, um das Token zusätzlich zu validieren (oder abzulehnen). Um gültig zu sein, darf die Variable $jwt_status
nicht leer sein und nicht gleich0
(null) .
Karte $jwt_claim_sub $jwt_status { "Anführungszeichen" 0; "Test" 0; Standard 1; } Server { abhören 80; Standort /products/ { auth_jwt "Produkte-API"; auth_jwt_key_file conf/api_secret.jwk; auth_jwt_require $jwt_status; Proxy-Pass http://API-Server; } }
JSON Web Tokens eignen sich gut, um authentifizierten Zugriff auf APIs bereitzustellen. Für den API-Client-Entwickler sind sie genauso einfach zu handhaben wie herkömmliche API-Schlüssel und liefern dem API-Gateway Identitätsinformationen, für die sonst eine Datenbanksuche erforderlich wäre. NGINX Plus bietet Unterstützung für JWT-Authentifizierung und anspruchsvolle Konfigurationslösungen basierend auf den im JWT selbst enthaltenen Informationen. In Kombination mit anderen API-Gateway- Funktionen ermöglicht Ihnen NGINX Plus die Bereitstellung API-basierter Dienste mit Geschwindigkeit, Zuverlässigkeit, Skalierbarkeit und Sicherheit.
Um JWT mit NGINX Plus selbst auszuprobieren, starten Sie noch heute Ihre kostenlose 30-Tage-Testversion oder kontaktieren Sie uns, um Ihre Anwendungsfälle zu besprechen .
„Dieser Blogbeitrag kann auf Produkte verweisen, die nicht mehr verfügbar und/oder nicht mehr unterstützt werden. Die aktuellsten Informationen zu verfügbaren F5 NGINX-Produkten und -Lösungen finden Sie in unserer NGINX-Produktfamilie . NGINX ist jetzt Teil von F5. Alle vorherigen NGINX.com-Links werden auf ähnliche NGINX-Inhalte auf F5.com umgeleitet."