Zum Inhalt

jwt: JWT Für Das Große nginx-module-lua

Installation

Wenn Sie das RPM-Repository-Abonnement noch nicht eingerichtet haben, melden Sie sich an. Dann können Sie mit den folgenden Schritten fortfahren.

CentOS/RHEL 7 oder Amazon Linux 2

yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install lua-resty-jwt

CentOS/RHEL 8+, Fedora Linux, Amazon Linux 2023

dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-jwt

Um diese Lua-Bibliothek mit NGINX zu verwenden, stellen Sie sicher, dass nginx-module-lua installiert ist.

Dieses Dokument beschreibt lua-resty-jwt v0.1.11, veröffentlicht am 11. Juli 2017.


Diese Bibliothek erfordert einen NGINX-Build mit OpenSSL, dem ngx_lua-Modul, dem LuaJIT 2.0, dem lua-resty-hmac und dem lua-resty-string.

Synopsis

    # nginx.conf:

    server {
        default_type text/plain;
        location = /verify {
            content_by_lua '
                local cjson = require "cjson"
                local jwt = require "resty.jwt"

                local jwt_token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9" ..
                    ".eyJmb28iOiJiYXIifQ" ..
                    ".VAoRL1IU0nOguxURF2ZcKR0SGKE1gCbqwyh8u2MLAyY"
                local jwt_obj = jwt:verify("lua-resty-jwt", jwt_token)
                ngx.say(cjson.encode(jwt_obj))
            ';
        }
        location = /sign {
            content_by_lua '
                local cjson = require "cjson"
                local jwt = require "resty.jwt"

                local jwt_token = jwt:sign(
                    "lua-resty-jwt",
                    {
                        header={typ="JWT", alg="HS256"},
                        payload={foo="bar"}
                    }
                )
                ngx.say(jwt_token)
            ';
        }
    }

Methoden

Um diese Bibliothek zu laden,

  1. müssen Sie den Pfad dieser Bibliothek in der ngx_lua lua_package_path Direktive angeben. Zum Beispiel, lua_package_path "/path/to/lua-resty-jwt/lib/?.lua;;";.
  2. verwenden Sie require, um die Bibliothek in eine lokale Lua-Variable zu laden:
    local jwt = require "resty.jwt"

sign

syntax: local jwt_token = jwt:sign(key, table_of_jwt)

signiert eine table_of_jwt zu einem jwt_token.

Das Argument alg gibt an, welcher Hash-Algorithmus verwendet werden soll (HS256, HS512, RS256).

Beispiel einer table_of_jwt

{
    "header": {"typ": "JWT", "alg": "HS512"},
    "payload": {"foo": "bar"}
}

verify

syntax: local jwt_obj = jwt:verify(key, jwt_token [, claim_spec [, ...]])

verifiziert einen jwt_token und gibt eine jwt_obj-Tabelle zurück. key kann ein vorab geteilter Schlüssel (als String) sein, oder eine Funktion, die einen einzelnen Parameter (den Wert von kid aus dem Header) entgegennimmt und entweder den vorab geteilten Schlüssel (als String) für den kid oder nil zurückgibt, wenn die kid-Suche fehlgeschlagen ist. Dieser Aufruf schlägt fehl, wenn Sie versuchen, eine Funktion für key anzugeben und kein kid im Header vorhanden ist.

Siehe Verification für Details zum Format der claim_spec-Parameter.

load & verify

syntax: local jwt_obj = jwt:load_jwt(jwt_token)
syntax: local verified = jwt:verify_jwt_obj(key, jwt_obj [, claim_spec [, ...]])

verify = load_jwt + verify_jwt_obj

lädt jwt, überprüft auf kid und verifiziert es dann mit dem richtigen Schlüssel.

Beispiel eines jwt_obj

{
    "raw_header": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9",
    "raw_payload": "eyJmb28iOiJiYXIifQ",
    "signature": "wrong-signature",
    "header": {"typ": "JWT", "alg": "HS256"},
    "payload": {"foo": "bar"},
    "verified": false,
    "valid": true,
    "reason": "signature mismatched: wrong-signature"
}

sign-jwe

syntax: local jwt_token = jwt:sign(key, table_of_jwt)

signiert eine table_of_jwt zu einem jwt_token.

Das Argument alg gibt an, welcher Hash-Algorithmus zum Verschlüsseln des Schlüssels verwendet werden soll (dir). Das Argument enc gibt an, welcher Hash-Algorithmus zum Verschlüsseln des Payloads verwendet werden soll (A128CBC-HS256, A256CBC-HS512).

Beispiel einer table_of_jwt

{
    "header": {"typ": "JWE", "alg": "dir", "enc":"A128CBC-HS256"},
    "payload": {"foo": "bar"}
}

Verification

Sowohl die Funktionen jwt:load als auch jwt:verify_jwt_obj nehmen als zusätzliche Parameter beliebig viele optionale claim_spec-Parameter. Ein claim_spec ist einfach eine Lua-Tabelle von Ansprüchen und Validatoren. Jeder Schlüssel in der claim_spec-Tabelle entspricht einem passenden Schlüssel im Payload, und der validator ist eine Funktion, die aufgerufen wird, um zu bestimmen, ob die Ansprüche erfüllt sind.

Die Signatur einer validator-Funktion ist:

function(val, claim, jwt_json)

Dabei ist val der Wert des Anspruchs aus dem getesteten jwt_obj (oder nil, wenn er nicht im Payload des Objekts vorhanden ist), claim ist der Name des Anspruchs, der verifiziert wird, und jwt_json ist eine json-serialisierte Darstellung des Objekts, das verifiziert wird. Wenn die Funktion die Parameter claim oder jwt_json nicht benötigt, können sie weggelassen werden.

Eine validator-Funktion gibt entweder true oder false zurück. Jeder validator KANN einen Fehler auslösen, und die Validierung wird als fehlgeschlagen betrachtet, und der ausgelöste Fehler wird in das Grundfeld des resultierenden Objekts eingefügt. Wenn ein validator nichts zurückgibt (d.h. nil), wird die Funktion als erfolgreich betrachtet - unter der Annahme, dass sie einen Fehler ausgelöst hätte, wenn sie fehlgeschlagen wäre.

Ein spezieller Anspruch namens __jwt kann verwendet werden, sodass, wenn eine validator-Funktion dafür existiert, die validator mit einem tiefen Klon des gesamten geparsten jwt-Objekts als Wert von val aufgerufen wird. Dies ermöglicht es Ihnen, Überprüfungen für ein ganzes Objekt zu schreiben, die von einem oder mehreren Ansprüchen abhängen können.

Mehrere claim_spec-Tabellen können an die Funktionen jwt:load und jwt:verify_jwt_obj übergeben werden - und sie werden in der Reihenfolge ausgeführt. Es gibt keine Garantie für die Ausführungsreihenfolge einzelner validators innerhalb eines einzelnen claim_spec. Wenn ein claim_spec fehlschlägt, werden alle folgenden claim_specs NICHT ausgeführt.

Beispiel claim_spec

{
    sub = function(val) return string.match("^[a-z]+$", val) end,
    iss = function(val)
        for _, value in pairs({ "first", "second" }) do
            if value == val then return true end
        end
        return false
    end,
    __jwt = function(val, claim, jwt_json)
        if val.payload.foo == nil or val.payload.bar == nil then
            error("Need to specify either 'foo' or 'bar'")
        end
    end
}

JWT-Validatoren

Eine Bibliothek hilfreicher validator-Funktionen existiert in resty.jwt-validators. Sie können diese Bibliothek verwenden, indem Sie Folgendes einfügen:

local validators = require "resty.jwt-validators"

Die folgenden Funktionen sind derzeit in der Validator-Bibliothek definiert. Die mit "(opt)" gekennzeichneten Funktionen haben denselben Namen wie opt_<name>, die dieselben Parameter akzeptiert. Die "opt"-Version der Funktion gibt true zurück, wenn der Schlüssel im Payload des zu verifizierenden jwt_object nicht existiert, während die "non-opt"-Version der Funktion false zurückgibt, wenn der Schlüssel nicht im Payload des zu verifizierenden jwt_object existiert.

validators.chain(...)

Gibt einen Validator zurück, der die angegebenen Funktionen nacheinander verknüpft - solange sie ihre Überprüfungen bestehen.

validators.required(chain_function)

Gibt einen Validator zurück, der false zurückgibt, wenn ein Wert nicht existiert. Wenn der Wert existiert und eine chain_function angegeben ist, wird der Wert von chain_function(val, claim, jwt_json) zurückgegeben, andernfalls wird true zurückgegeben. Dies ermöglicht es, anzugeben, dass ein Wert sowohl erforderlich als auch eine zusätzliche Überprüfung bestehen muss.

validators.require_one_of(claim_keys)

Gibt einen Validator zurück, der mit einer Nachricht einen Fehler auslöst, wenn KEIN der angegebenen Anspruchsschlüssel existiert. Es wird erwartet, dass diese Funktion gegen ein vollständiges jwt-Objekt verwendet wird. Die claim_keys müssen eine nicht leere Tabelle von Strings sein.

validators.check(check_val, check_function, name, check_type) (opt)

Gibt einen Validator zurück, der überprüft, ob das Ergebnis des Aufrufs der angegebenen check_function für den getesteten Wert und check_val true zurückgibt. Die Werte von check_val und check_function dürfen nicht nil sein. Der optionale name wird für Fehlermeldungen verwendet und hat standardmäßig den Wert "check_value". Der optionale check_type wird verwendet, um sicherzustellen, dass der Überprüfungstyp übereinstimmt und hat standardmäßig den Wert type(check_val). Der erste Parameter, der an die check_function übergeben wird, wird niemals nil sein. Wenn die check_function einen Fehler auslöst, wird dieser zur Fehlermeldung hinzugefügt.

validators.equals(check_val) (opt)

Gibt einen Validator zurück, der überprüft, ob ein Wert genau (unter Verwendung von ==) dem angegebenen check_value entspricht. Der Wert von check_val darf nicht nil sein.

validators.matches(pattern) (opt)

Gibt einen Validator zurück, der überprüft, ob ein Wert dem angegebenen Muster (unter Verwendung von string.match) entspricht. Der Wert von pattern muss ein String sein.

validators.any_of(check_values, check_function, name, check_type, table_type) (opt)

Gibt einen Validator zurück, der die angegebene check_function für jeden der angegebenen check_values und den getesteten Wert aufruft. Wenn einer dieser Aufrufe true zurückgibt, gibt diese Funktion true zurück. Der Wert von check_values muss eine nicht leere Tabelle mit allen gleichen Typen sein, und der Wert von check_function darf nicht nil sein. Der optionale name wird für Fehlermeldungen verwendet und hat standardmäßig den Wert "check_values". Der optionale check_type wird verwendet, um sicherzustellen, dass der Überprüfungstyp übereinstimmt und hat standardmäßig den Wert type(check_values[1]) - den Tabellentyp.

validators.equals_any_of(check_values) (opt)

Gibt einen Validator zurück, der überprüft, ob ein Wert genau einem der angegebenen check_values entspricht.

validators.matches_any_of(patterns) (opt)

Gibt einen Validator zurück, der überprüft, ob ein Wert einem der angegebenen patterns entspricht.

validators.contains_any_of(check_values,name) (opt)

Gibt einen Validator zurück, der überprüft, ob ein Wert des erwarteten Typs string in einem der angegebenen check_values existiert. Der Wert von check_values muss eine nicht leere Tabelle mit allen gleichen Typen sein. Der optionale Name wird für Fehlermeldungen verwendet und hat standardmäßig den Wert check_values.

validators.greater_than(check_val) (opt)

Gibt einen Validator zurück, der überprüft, wie ein Wert (numerisch, unter Verwendung von >) mit einem gegebenen check_value verglichen wird. Der Wert von check_val darf nicht nil sein und muss eine Zahl sein.

validators.greater_than_or_equal(check_val) (opt)

Gibt einen Validator zurück, der überprüft, wie ein Wert (numerisch, unter Verwendung von >=) mit einem gegebenen check_value verglichen wird. Der Wert von check_val darf nicht nil sein und muss eine Zahl sein.

validators.less_than(check_val) (opt)

Gibt einen Validator zurück, der überprüft, wie ein Wert (numerisch, unter Verwendung von <) mit einem gegebenen check_value verglichen wird. Der Wert von check_val darf nicht nil sein und muss eine Zahl sein.

validators.less_than_or_equal(check_val) (opt)

Gibt einen Validator zurück, der überprüft, wie ein Wert (numerisch, unter Verwendung von <=) mit einem gegebenen check_value verglichen wird. Der Wert von check_val darf nicht nil sein und muss eine Zahl sein.

validators.is_not_before() (opt)

Gibt einen Validator zurück, der überprüft, ob die aktuelle Zeit nicht vor dem getesteten Wert innerhalb der Toleranz des Systems liegt. Das bedeutet:

val <= (system_clock() + system_leeway).

validators.is_not_expired() (opt)

Gibt einen Validator zurück, der überprüft, ob die aktuelle Zeit nicht gleich oder nach dem getesteten Wert innerhalb der Toleranz des Systems liegt. Das bedeutet:

val > (system_clock() - system_leeway).

validators.is_at() (opt)

Gibt einen Validator zurück, der überprüft, ob die aktuelle Zeit gleich dem getesteten Wert innerhalb der Toleranz des Systems ist. Das bedeutet:

val >= (system_clock() - system_leeway) and val <= (system_clock() + system_leeway).

validators.set_system_leeway(leeway)

Eine Funktion, um die Toleranz (in Sekunden) für is_not_before und is_not_expired festzulegen. Der Standardwert ist 0 Sekunden.

validators.set_system_clock(clock)

Eine Funktion, um die Systemuhr für is_not_before und is_not_expired festzulegen. Der Standardwert ist ngx.now.

Beispiel claim_spec, das Validatoren verwendet

local validators = require "resty.jwt-validators"
local claim_spec = {
    sub = validators.opt_matches("^[a-z]+$"),
    iss = validators.equals_any_of({ "first", "second" }),
    __jwt = validators.require_one_of({ "foo", "bar" })
}

Legacy-/Zeitrahmenoptionen

Um Code zu unterstützen, der frühere Versionen dieser Bibliothek verwendet hat, sowie um das Angeben von zeitrahmenbasierten claim_specs zu vereinfachen, können Sie anstelle eines einzelnen claim_spec-Parameters eine Tabelle von validation_options verwenden. Der Parameter sollte als Schlüssel/Wert-Tabelle ausgedrückt werden. Jeder Schlüssel der Tabelle sollte aus der folgenden Liste ausgewählt werden.

Beim Verwenden von Legacy-validation_options MÜSSEN SIE NUR diese Optionen angeben. Das bedeutet, dass Sie keine Legacy-validation_options mit anderen claim_spec-Validatoren mischen können. Um dies zu erreichen, müssen Sie mehrere Optionen an die Funktionen jwt:load/jwt:verify_jwt_obj angeben.

  • lifetime_grace_period: Definieren Sie die Toleranz in Sekunden, um Uhrenabweichungen zwischen dem Server, der das jwt generiert hat, und dem Server, der es validiert. Der Wert sollte null (0) oder eine positive ganze Zahl sein.

    • Wenn diese Validierungsoption angegeben wird, stellt der Prozess sicher, dass das jwt mindestens einen der beiden Ansprüche nbf oder exp enthält und vergleicht die aktuelle Uhrzeit mit diesen Grenzen. Sollte das jwt als abgelaufen oder noch nicht gültig angesehen werden, schlägt die Überprüfung fehl.

    • Wenn keiner der Ansprüche nbf und exp gefunden werden kann, schlägt die Überprüfung fehl.

    • Die Ansprüche nbf und exp sollten im jwt als numerische Werte ausgedrückt werden. Sollte dies nicht der Fall sein, schlägt die Überprüfung fehl.

    • Das Angeben dieser Option entspricht dem Aufruf von:

      validators.set_system_leeway(leeway)
      

    und dem Angeben als claim_spec:

    {
      __jwt = validators.require_one_of({ "nbf", "exp" }),
      nbf = validators.opt_is_not_before(),
      exp = validators.opt_is_not_expired()
    }
    

  • require_nbf_claim: Geben Sie an, ob der Anspruch nbf optional ist oder nicht. Der Wert sollte ein Boolean sein.

    • Wenn diese Validierungsoption auf true gesetzt ist und kein lifetime_grace_period angegeben wurde, wird eine Null (0) Toleranz impliziert.

    • Das Angeben dieser Option entspricht dem Angeben als claim_spec:

      {
        nbf = validators.is_not_before(),
      }
      

  • require_exp_claim: Geben Sie an, ob der Anspruch exp optional ist oder nicht. Der Wert sollte ein Boolean sein.

    • Wenn diese Validierungsoption auf true gesetzt ist und kein lifetime_grace_period angegeben wurde, wird eine Null (0) Toleranz impliziert.

    • Das Angeben dieser Option entspricht dem Angeben als claim_spec:

      {
        exp = validators.is_not_expired(),
      }
      

  • valid_issuers: Whitelist der geprüften Aussteller des jwt. Der Wert sollte ein Array von Strings sein.

    • Wenn diese Validierungsoption angegeben wird, vergleicht der Prozess den Anspruch iss des jwt mit der Liste der gültigen Aussteller. Der Vergleich erfolgt in einer Groß-/Kleinschreibung empfindlichen Weise. Sollte der Aussteller des jwt nicht in der Whitelist gefunden werden, schlägt die Überprüfung fehl.

    • Der Anspruch iss sollte im jwt als String ausgedrückt werden. Sollte dies nicht der Fall sein, schlägt die Überprüfung fehl.

    • Das Angeben dieser Option entspricht dem Angeben als claim_spec:

      {
        iss = validators.equals_any_of(valid_issuers),
      }
      

Beispiel für die Verwendung von validation_options

local jwt_obj = jwt:verify(key, jwt_token,
    {
        lifetime_grace_period = 120,
        require_exp_claim = true,
        valid_issuers = { "my-trusted-issuer", "my-other-trusteed-issuer" }
    }
)

Beispiele

Testen Mit Docker

./ci script

Siehe Auch

GitHub

Sie finden zusätzliche Konfigurationstipps und Dokumentation für dieses Modul im GitHub-Repository für nginx-module-jwt.