BLOG | NGINX

Authentifizieren von API-Clients mit JWT und NGINX Plus

NGINX-Teil-von-F5-horiz-schwarz-Typ-RGB
Liam Crilly Miniaturbild
Liam Crilly
Veröffentlicht am 02. Dezember 2021

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> .

Anatomie eines JWT

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
pXVCJ9
{
"alg": "HS256",
"typ": "JWT"
}
Nutzlast ewogICAgInN1YiI6ICJsYzEiLAogICAgImVtYWlsIjogImxpYW0uY3JpbGx5QG5naW54LmNvbSIsCn0= {
"sub": "lc1",
"E-Mail": "liam.crilly@nginx.com",
}

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.

JWT als API-Schlüssel

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.

API-Client und JWT-Authentifizierung mit einem herkömmlichen API-Schlüssel
Das API-Gateway validiert den API-Schlüssel, indem es ein Schlüsselregister konsultiert, bevor es die Anforderung weiterleitet
zum API-Endpunkt

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.

API-Client und JWT-Authentifizierung mit JWT und NGINX Plus
NGINX Plus validiert das JWT, bevor die Anfrage an die API-Endpunkte weitergeleitet wird

Konfigurieren von NGINX Plus als authentifizierendes API-Gateway

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.

Ausgeben eines JWT an API-Clients

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.

  1. 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
    
  2. 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
    
  3. 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
    
  4. Hängen Sie die codierte Signatur an den Header und die Nutzlast an.

    $ echo $HEADER_PAYLOAD.ggVOHYnVFB8GVPE-VOIo3jD71gTkLffAY0hQOGXPL2I > Zitate.jwt
    
  5. 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.

Nutzung von JWT-Ansprüchen für Protokollierung und Ratenbegrenzung

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.

Widerrufen von JWTs

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; } }

Zusammenfassung

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."