コンテンツにスキップ

JWT@認証

はじめに

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


01. JWT:JSON Web Token

JWTとは

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

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

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


JWT仕様のトークンの種類

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

  • JWT仕様のアクセストークン
  • IDトークン (必ずJWT仕様)


02. JWTによるトークンベース伝播による認証の仕組み

JWTの検証とは

JWT仕様のトークン (例:IDトークン) の有効期限や発行元認証局が正しいかどうかを検証する。

署名の仕組みを使用して、発行元認証局を検証する。


署名が共通鍵方式の場合

▼ 共通鍵方式の特徴

共通鍵方式では、秘密鍵を使用して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


署名が公開鍵方式の場合

▼ 公開鍵方式の特徴

共通鍵方式では、秘密鍵を使用して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が有効か否かを検証する。
  5. JWTが有効であれば、アプリケーションの認証処理は成功とし、ユーザーにレスポンスを返信する

jwt_public_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の作成

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を設定する。

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

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

▼ 署名の場合

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

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


05. JWTの保持方法と安全度の比較

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


06. JWTの代替

  • fernet token
  • branca-token
  • PASETO