JSON Web Token (JWT、「ジョット」と発音) は、ID 情報を交換するためのコンパクトで移植性が高い手段です。 JWT 仕様はOpenID Connectの重要な基盤であり、OAuth 2.0 エコシステムにシングル サインオン トークンを提供します。 JWT は、それ自体が認証資格情報としても使用でき、従来の API キーよりも Web ベースの API へのアクセスを制御するのに適した方法です。
NGINX Plus R10 以降では、JWT を直接検証できます。 このブログ記事では、NGINX Plus を API ゲートウェイとして使用し、API エンドポイントへのフロントエンドを提供し、JWT を使用してクライアント アプリケーションを認証する方法について説明します。
ネイティブ JWT サポートは NGINX Plus でのみ利用可能であり、NGINX Open Source では利用できません。
エディタ – このブログ投稿は 更新されました 2021年12月に 認証_jwt_require
指令は NGINX プラス R25。 ディレクティブの詳細については、 NGINX Plus R25 を発表したブログの「カスタム JWT 検証ルール」を参照してください。
NGINX Plus R15以降では、OpenID Connect 1.0 の「認証コードフロー」も制御できるため、ほとんどの主要な ID プロバイダーとの統合が可能になります。 詳細については、 「NGINX Plus R15 の発表」<.htmlspan>を参照してください。
JWT には、ヘッダー、ペイロード、署名の 3 つの部分があります。 送信時には次のようになります。 読みやすくするために改行を追加し(実際の JWT は単一の文字列です)、3 つの部分を区別するために色分けしました。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 . ewogICAgInN1YiI6ICJsYzEiLAogICAgImVtYWlsIjogImxpYW0uY3JpbGx5QG5naW54LmNvbSIsCn0= . VGYHWPterIaLjRi0LywgN3jnDUQbSsFptUw99g2slfc
示されているように、ピリオド ( .
) はヘッダー、ペイロード、および署名を区切ります。 ヘッダーとペイロードはBase64 でエンコードされたJSON オブジェクトです。 署名はalg
ヘッダーで指定されたアルゴリズムを使用して暗号化されます。これはサンプル JWT をデコードすると確認できます。
エンコード | 解読 | |
---|---|---|
ヘッダ | eyJhbGciOiJIUzI1NiIsInR5cCI6Ik |
{ |
ペイロード | ewogICAgInN1YiI6ICJsYzEiLAogICAgImVtYWlsIjogImxpYW0uY3JpbGx5QG5naW54LmNvbSIsCn0= |
{ |
JWT 標準では、いくつかの署名アルゴリズムが定義されています。 この例のHS256 という
値は HMAC SHA-256 を指し、このブログ投稿のすべてのサンプル JWT にこれを使用しています。 NGINX Plus は、標準で定義されているHS xxx
、 RS xxx
、およびES xxx
署名アルゴリズムをサポートしています。 JWT に暗号署名できるため、認証資格情報として使用するのに最適です。
API クライアント (API リソースを要求するリモート ソフトウェア クライアント) を認証する一般的な方法は、一般にAPI キーと呼ばれる共有シークレットを使用することです。 従来の API キーは、本質的には、クライアントがリクエストごとに追加の HTTP ヘッダーとして送信する長くて複雑なパスワードです。 提供された API キーが有効なキーのリストに含まれている場合、API エンドポイントは要求されたリソースへのアクセスを許可します。 通常、API エンドポイントは API キー自体を検証しません。代わりに、API ゲートウェイが認証プロセスを処理し、各リクエストを適切なエンドポイントにルーティングします。 これにより、計算のオフロードに加えて、高可用性や多数の API エンドポイントへの負荷分散など、リバース プロキシに伴う利点も得られます。
異なる API クライアントに異なるアクセス制御とポリシーを適用するのは一般的です。 従来の API キーでは、API キーを属性セットと一致させるための検索が必要です。 すべてのリクエストに対してこの検索を実行すると、システム全体のレイテンシに当然の影響が及びます。 JWT では、これらの属性が埋め込まれているため、個別の検索が不要になります。
JWT を API キーとして使用すると、ベスト プラクティスの認証テクノロジと ID 属性を交換するための標準ベースのスキーマを組み合わせた、従来の API キーに代わる高性能な手段が提供されます。
JWT を検証するための NGINX Plus の設定は非常にシンプルです。
アップストリーム api_server {
server 10.0.0.1;
server 10.0.0.2;
}
server {
listen 80;
location /products/ {
auth_jwt "製品 API";
auth_jwt_key_file conf/api_secret.jwk;
proxy_pass http://api_server;
}
}
最初に行うことは、API エンドポイントをホストするサーバーのアドレスをアップストリーム
ブロックに指定することです。 ロケーション
ブロックは、 /products/で始まる URL へのすべてのリクエストを認証する必要があることを指定します。 auth_jwt
ディレクティブは、返される認証レルムを定義します(401
認証が失敗した場合はステータス コードを返します。
auth_jwt_key_file
ディレクティブは、NGINX Plus に JWT の署名要素を検証する方法を指示します。 この例では、HMAC SHA-256 アルゴリズムを使用して JWT に署名するため、署名に使用する対称キーを格納する JSON Web キーをconf/api_secret.jwkに作成する必要があります。 ファイルはJSON Web Key 仕様で説明されている形式に従う必要があります。例は次のようになります。
{"keys":
[{
"k":"ZmFudGFzdGljand0",
"kty":"oct",
"kid":"0001"
}]
}
対称キーはk
フィールドで定義されており、プレーンテキスト文字列fantasticjwt
のBase64URL エンコードされた値がここにあります。 次のコマンドを実行してエンコードされた値を取得しました。
$ echo -n fantasticjwt | base64 | tr '+/' '-_' | tr -d '='
kty
フィールドは、キー タイプを対称キー (オクテット シーケンス) として定義します。 最後に、 kid
(Key ID)フィールドは、このJSON Web Keyのシリアル番号を定義します。0001
これにより、同じファイル ( auth_jwt_key_file
ディレクティブで名前が付けられる) 内で複数のキーをサポートし、それらのキーとそれらで署名された JWT のライフサイクルを管理できるようになります。
これで、API クライアントに JWT を発行する準備が整いました。
サンプル API クライアントとして、「見積システム」アプリケーションを使用し、API クライアント用の JWT を作成します。 まず、JWT ヘッダーを定義します。
{
"typ":"JWT",
"alg":"HS256",
"kid":"0001"
}
typ
フィールドはタイプを JSON Web Token として定義し、 alg
フィールドは JWT が HMAC SHA256 アルゴリズムで署名されることを指定し、 kid
フィールドは JWT がそのシリアル番号を持つ JSON Web キーで署名されることを指定します。
次に、JWT ペイロードを定義します。
{
"name":"見積システム",
"sub":"quotes",
"iss":"私の API ゲートウェイ"
}
sub
(件名) フィールドは、 name
フィールドの完全な値に対する一意の識別子です。 iss
フィールドは JWT の発行者を表します。これは、API ゲートウェイがサードパーティの発行者または集中型 ID 管理システムからの JWT も受け入れる場合に役立ちます。
JWT を作成するために必要なものがすべて揃ったので、次の手順に従って JWT を正しくエンコードして署名します。 コマンドとエンコードされた値は、読みやすくするために複数行で表示されます。実際には、それぞれが 1 行で入力または表示されます。
ヘッダーとペイロードを別々にフラット化し、Base64URL エンコードします。
$ echo -n '{"typ":"JWT","alg":"HS256","kid":"0001"}' | base64 | tr '+/' '-_' | tr -d '=' eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjAwMDEifQ $ echo -n '{"name":"Quotation System","sub":"quotes","iss":"My API Gateway"}' | base64 | tr '+/' '-_' | tr -d '=' eyJuYW1lIjoiUXVvdGF0aW9uIFN5c3RlbSIsInN1YiI6InF1b3RlcyIsImlzcyI6Ik 15IEFQSSBHYXRld2F5In0
エンコードされたヘッダーとペイロードをピリオド (.) で連結し、その結果をHEADER_PAYLOAD
変数に割り当てます。
$ HEADER_PAYLOAD=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjAwMDEifQ.eyJuYW1lIjoiUXVvdGF0aW9uIFN5c3RlbSIsInN1YiI6InF1b3RlcyIsIm lzcyI6Ik15IEFQSSBHYXRld2F5In0
対称キーを使用してヘッダーとペイロードに署名し、署名を Base64URL エンコードします。
$ echo -n $HEADER_PAYLOAD | openssl dgst -binary -sha256 -hmac fantasticjwt | base64 | tr '+/' '-_' | tr -d '=' ggVOHYnVFB8GVPE-VOIo3jD71gTkLffAY0hQOGXPL2I
エンコードされた署名をヘッダーとペイロードに追加します。
$ echo $HEADER_PAYLOAD.ggVOHYnVFB8GVPE-VOIo3jD71gTkLffAY0hQOGXPL2I > quotes.jwt
API ゲートウェイに認証されたリクエストを送信してテストします (この例では、ゲートウェイは localhost 上で実行されています)。
$ curl -H "認証: ベアラー `cat quotes.jwt`" http://localhost/products/widget1
ステップ 5 のcurl
コマンドは、NGINX Plus がデフォルトで期待するBearer Tokenの形式で JWT を NGINX Plus に送信します。 NGINX Plus は、Cookie またはクエリ文字列パラメータから JWT を取得することもできます。これを設定するには、 auth_jwt
ディレクティブにtoken=
パラメータを含めます。 たとえば、次の構成では、NGINX Plus はこのcurl
コマンドで送信された JWT を検証できます。
$ curl http://localhost/products/widget1?apijwt=`cat quotes.jwt`
server { listen 80; location /products/ { auth_jwt "Products API" token=$arg_apijwt ; auth_jwt_key_file conf/api_secret.jwk; proxy_pass http://api_server; } }
NGINX Plus を設定し、上記のように JWT を生成して検証したら、JWT を API クライアント開発者に送信し、各 API リクエストで JWT を送信するために使用されるメカニズムについて合意する準備が整います。
認証資格情報としての JWT の主な利点の 1 つは、JWT とそのペイロードに関連付けられたエンティティ (発行者、発行先のユーザー、対象受信者など) を表す「クレーム」を伝達できることです。 JWT を検証した後、NGINX Plus はヘッダーとペイロードに存在するすべてのフィールドに変数としてアクセスできるようになります。 これらにアクセスするには、目的のフィールドに$jwt_header_
または$jwt_claim_ を
プレフィックスとして付けます (たとえば、サブ
クレームの場合は$jwt_claim_sub
)。 つまり、API 自体に JWT 処理を実装する必要なく、JWT 内に含まれる情報を API エンドポイントに非常に簡単にプロキシできるということです。
この構成例では、いくつかの高度な機能を示します。
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 { listen 80; location /products/ { auth_jwt "製品 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; access_log /var/log/nginx/access_jwt.log jwt; } }
log_format
ディレクティブは、共通ログ形式を 2 つの追加フィールド$jwt_header_alg
と$jwt_claim_sub
で拡張するjwt
と呼ばれる新しい形式を定義します。 location
ブロック内では、 access_log
ディレクティブを使用して、検証された JWT から取得した値を含むログを書き込みます。
この例では、クレームベースの変数を使用して、IP アドレスごとではなく、API クライアントごとに API レート制限を提供しています。 これは、複数の API クライアントが単一のポータルに埋め込まれており、IP アドレスで区別できない場合に特に便利です。 limit_req_zone
ディレクティブは、レート制限を計算するためのキーとして JWTサブ
クレームを使用し、その後、 limit_req
ディレクティブを含めることで、ロケーション
ブロックにレート制限を適用します。
最後に、リクエストが API エンドポイントにプロキシされるときに、JWT サブジェクトを新しい HTTP ヘッダーとして提供します。 proxy_set_header
ディレクティブは、API エンドポイントが簡単に使用できるAPI‑Client
という HTTP ヘッダーを追加します。 したがって、API エンドポイントは JWT 処理ロジックを実装する必要はありません。 API エンドポイントの数が増えるにつれて、これはますます価値が高まります。
場合によっては、API クライアントの JWT を取り消したり再発行したりする必要が生じることがあります。 シンプルなマップ
ブロックとauth_jwt_require
ディレクティブを組み合わせることで、JWT の有効期限 ( exp
クレームで表されます) に達するまで JWT を無効としてマークし、その時点でその JWT のマップ
エントリを安全に削除することで、API クライアントへのアクセスを拒否できます。
この例では、 $jwt_status
変数を次のように設定しています。0
または1
トークン内のサブ
クレームの値( $jwt_claim_sub
変数でキャプチャされたもの)に応じて異なります。 次に、 location
ブロックでauth_jwt_require
ディレクティブを使用して、トークンをさらに検証 (または拒否) します。 有効であるためには、 $jwt_ステータス
変数は空ではなく、等しくない に 0
(ゼロ)。
map $jwt_claim_sub $jwt_status { "quotes" 0; "test" 0; default 1; } server { listen 80; location /products/ { auth_jwt "Products API"; auth_jwt_key_file conf/api_secret.jwk; auth_jwt_require $jwt_status; proxy_pass http://api_server; } }
JSON Web Token は、API への認証されたアクセスを提供するのに適しています。 API クライアント開発者にとって、これらは従来の API キーと同様に簡単に扱うことができ、データベース検索を必要とする ID 情報を API ゲートウェイに提供します。 NGINX Plus は、JWT 自体に含まれる情報に基づいて、JWT 認証と高度な構成ソリューションをサポートします。 NGINX Plus を他のAPI ゲートウェイ機能と組み合わせることで、速度、信頼性、拡張性、セキュリティを備えた API ベースのサービスを提供できるようになります。
NGINX Plus で JWT を試すには、今すぐ30 日間の無料トライアルを開始するか、弊社にお問い合わせの上、ユースケースについてご相談ください。
「このブログ投稿には、入手できなくなった製品やサポートされなくなった製品が参照されている場合があります。 利用可能な F5 NGINX 製品およびソリューションに関する最新情報については、 NGINX 製品ファミリーをご覧ください。 NGINX は現在 F5 の一部です。 以前の NGINX.com リンクはすべて、F5.com の同様の NGINX コンテンツにリダイレクトされます。"