コンテンツにスキップ

JWT@認証

はじめに

本サイトにつきまして、以下をご認識のほど宜しくお願いいたします。


01. JWT:JSON Web Token

JWTとは

『ヘッダー』『ペイロード』『署名』のそれぞれのJSON型データをbase64方式によってエンコードし、ドットでつないだトークン。

JWTでエンコードされたトークンは、JWT仕様トークンという。

Bear認証やOAuthのトークンとして使用できる。

ランダムな文字列をこれら認証のトークンとするより、JWTを使用した方がより安全である。


04. JWT仕様トークンの構造

JWT仕様トークンの作成

JWTは以下のサイトから取得できる。

例えばJavaScriptであれば、以下のような処理を実行し、JWTを作成する。

// <ヘッダーエンコード値>.<ペイロードエンコード値>.<署名エンコード値>
const token =
  base64urlEncoding(header) +
  "." +
  base64urlEncoding(payload) +
  "." +
  base64urlEncoding(signature);


JWT仕様トークンの構造

▼ ヘッダー

ヘッダーは以下のJSON型データで定義される。

署名のための暗号アルゴリズムは、『HS256 (共通鍵方式) 』『RS256 (公開鍵方式) 』『ES256』『none (署名なし) 』から選択できる。

const header = {
  typ: "JWT", // JWT仕様トークンの使用
  alg: "HS256", // 署名のための暗号アルゴリズム
};

▼ ペイロード

ペイロードは以下のJSON型データで定義される。

ペイロードには、実際に送信したいJSONを設定する。

必ず設定しなければならない『予約済みクレーム』と、ユーザー側が自由に定義できる『プライベートクレーム』がある。

パラメーター名 対応するクレーム ユーザーが設定する 役割
aud Audience
exp Expiration Time JWT仕様トークンの有効期限を表す。
iat Issuer At 発行された時刻を表す
iss Issuer JWT仕様トークンの発行元認証局を表す。
jti JWT ID
sub Subject 一意な識別子を表す。 ユーザーID
const payload = {
  aud: "foo",
  exp: 1452565628,
  iat: 1452565568,
  iss: "https://example.com",
  jti: "foo",
  sub: "foo",
};

▼ 署名の場合

例えばJavaScriptであれば、以下のような処理が実行されている。

JWTを検証する場合、この署名の値を使用する。

const signature = HMACSHA256(
  base64urlEncoding(header) + "." + base64urlEncoding(payload),
  secret,
);


JWT仕様トークンの種類

JWT仕様トークンには以下の種類がある。

トークンの種類 トークンの情報タイプ
アクセストークン IDプロバイダーのツールによってはJWT仕様 (例:Keycloak) なためSelf-containedトークン
IDトークン 必ずJWT仕様であり、Self-containedトークン


02. JWT仕様トークンの署名検証の仕組み

署名検証とは

JWT仕様トークン (例:IDトークン) の情報 (署名部分、有効期限、発行元認証局など) から、JWT仕様トークンの有効性を検証できる。


検証方法の種類

JWTの署名の検証方法には以下があり、公開鍵による検証が一般的である。

以下のいずれかの方法で検証できる。

  • 認可サーバーから取得した公開鍵 (一般的)
  • 認可サーバーから取得した共通鍵
  • 認可サーバーのイントロスペクションエンドポイント


署名が公開鍵方式の場合

▼ 公開鍵方式の特徴

共通鍵方式では、秘密鍵を使用してJWT作成 (初回認証時) 、公開鍵を使用してJWT検証 (次回認証時) を実施する。

例えば、SSOでは、JWT仕様トークンの署名が公開鍵方式である。

クライアント側に秘密鍵、IDプロバイダー側に公開鍵を配置する。

▼ 初回認証時

初回認証時は、JWTを作成する。

  1. ユーザーは、IDプロバイダーのログインフォームにユーザーIDとパスワードを入力する。
  2. IDプロバイダーは、フォームの入力情報を含むリクエストを受信し、認証処理を実行する。また、DBのユーザー情報と照合して認証処理を実行する。
  3. 認証が成功すれば、IDプロバイダーは秘密鍵を使用してJWTを作成する。
  4. IDプロバイダーは、JWTをレスポンスに含め、ユーザーに返信する。
  5. ブラウザは、JWTをLocalStorageやCookieに保管する。

jwt_public_generate_token

▼ 次回認証時

認証の成功状態を維持するため、初回認証時にブラウザの保管したJWTを再利用する。

  1. ユーザーは、ブラウザの保管したJWTをリクエストヘッダーに含め、アプリケーションに送信する。
  2. アプリケーションは、IDプロバイダーに公開鍵をリクエストする。
  3. IDプロバイダーは、アプリケーションに公開鍵をレスポンスする。
  4. アプリケーションは、IDプロバイダーから取得した公開鍵を使用してJWTの情報 (署名部分、有効期限、発行元認証局など) から、JWT仕様トークンの有効性を検証する。
  5. JWTが有効であれば、アプリケーションの認証処理は成功とし、ユーザーにレスポンスを返信する

jwt_public_verify_token


署名が共通鍵方式の場合

▼ 共通鍵方式の特徴

共通鍵方式では、秘密鍵を使用してJWT作成 (初回認証時) とJWT検証 (次回認証時) の両方を実施する。

例えば、SSOではない認証では、JWT仕様トークンの署名が公開鍵方式である。

ユーザー側とアプリケーション側に秘密鍵を配置する。

▼ 初回認証時

初回認証時は、JWTを作成する。

  1. ユーザーは、ログインフォームにユーザーIDとパスワードを入力する。
  2. アプリケーションは、フォームの入力情報を含むリクエストを受信する。また、DBのユーザー情報と照合して認証処理を実行する。
  3. 認証が成功すれば、アプリケーションは秘密鍵を使用してJWTを作成する。
  4. アプリケーションは、JWTをレスポンスに含め、ユーザーに返信する。
  5. ブラウザは、JWTをLocalStorageやCookieに保管する。

jwt_common_generate_token

▼ 次回認証時

認証の成功状態を維持するため、初回認証時にブラウザの保管したJWTを再利用する。

  1. ユーザーは、ブラウザの保管したJWTをリクエストヘッダーに含め、アプリケーションに送信する。
  2. アプリケーションは、秘密鍵を使用してJWTの署名を検証する。
  3. JWTが有効であれば、認証成功とし、ユーザーにレスポンスを返信する。

jwt_common_verify_token


03. JWT仕様トークンの運搬方法

HTTP認証の場合

▼ Bearer認証

トークンをAuthorizationヘッダーに割り当て、リクエストを送信する。

POST https://example.com/foo
---
authorization: Bearer <ヘッダーJSONエンコード値>.<ペイロードJSONエンコード値>.<署名JSONエンコード値>

AuthorizationヘッダーはCookieヘッダーとは異なり、ローカルマシンに保管できない。

なお、APIではリクエストの送受信時にCookieヘッダーよりもAuthorizationヘッダーの方が扱いやすいため、Authorizationヘッダーでトークンを運ぶことになる。

また、スマホアプリもCookieヘッダーよりAuthorizationヘッダーがいいらしい。


Form認証の場合

トークンをCookieヘッダーに割り当て、リクエストを送信する。

POST https://example.com/foo
---
cookie: Bearer <ヘッダーJSONエンコード値>.<ペイロードJSONエンコード値>.<署名JSONエンコード値>


04. JWT仕様トークンの保持方法と安全度の比較

クライアント保持方法 組み合わせ おすすめ度 コメント
Cookieヘッダー (ローカルマシンのディレクトリ) 、LocalStorage JWT仕様トークンのみ △ 〜 × いずれの方法でも、XSSによってJWTが盗まれる可能性がある。
Cookieヘッダー プリフライトリクエスト Access-Control-Max-Ageの期間内だとCSRFでJWTが盗まれる可能性がある。
Cookieヘッダー CSRFトークン
SameSiteCookie SPAとAPIが同一オリジンの必要がある。


05. JWT仕様トークンの代替

  • fernet token
  • branca-token
  • PASETO