📘

「よし、JWTを使おう!」となったら最初に考えること

2023/07/07に公開

私は認証トークンとしてJWTをよく使います。

初めてJWTを使った時は
「なんかよくわからないからコピペで使おう。なんかうまくいってるからヨシ!」
って感じでした。

でも何度も使ってると
「こうやればいいんだ」
ってだんだん理解ができてきます。

その際に「最初からちゃんと意識しておけばなー」って思ったことを過去の自分に伝えるつもりで記事にします。

その1:暗号化方式をどうするか

共通鍵にするか、対象鍵にするか、って話。今だとHS256(共通鍵)にするか、RS256(対象鍵)にするか、になる?

  • 共通鍵
    • JWTの作成、検証を同じ鍵で行う
    • 作成者、検証者が同じならこっちでOK
  • 対称鍵
    • 作成を秘密鍵、検証を公開鍵で行う
    • 作成者と検証者が違うならこっち
  • 要は鍵をどうするか、って話
  • コンサートのチケットで例えると対称鍵方式だと、入場のチケット確認をアルバイトにお願いすることができる(公開鍵をアルバイトに渡してチケットの確認をして貰うイメージ)
  • 共通鍵だとチケットは自分で確認しないといけない(共通鍵を他人に渡したら、勝手にチケットを作成される可能性がある)
  • ただし、公開鍵方式は「鍵を渡す」という行動が入る以上「公開鍵と間違えて秘密鍵渡しちゃった!」なんてミスが起こる可能性がある。
  • 共通鍵方式なら「鍵を渡す」という行為自体NGのため、「渡す鍵を間違えた」という心配をしなくていい。

その2:予約済みクレームをどうするか

  • JWTには予約済みクレームというものがある(予約語みたいなもの)
  • 予約済みクレームは使わなくてもいい
  • しかし「予約済みクレームを使うのか?使うとしたらどうするか?」を考えれば、トークンとして必要なものは大体揃う
  • 予約済みクレームを使うなら検証もちゃんと実装すること

iss

  • 「誰がこのJWTを発行したのか?」
  • コンサートチケットで例えると、iss: "○木坂46" や iss: "○もいろクローバーZ" など、このチケットはどのアイドルグループのものか、になる
  • これがないとそれぞれのグループのチケットを作るためにそれぞれの鍵を用意しないといけない

sub

  • 「このJWTのアプリ内での使い道は何?」
  • コンサートチケットで例えると、sub: "入場チケット" や sub: "グループ関係者" などになる
  • 入場チケットを使ってグループ関係者としてアイドルの控え室に行けたらやばいっすね、って話

aud

  • 「このJWTは誰向けのもの?」
  • isssubはJWT作成者が使うが、audは作成者以外が使う
  • コンサートチケットで例えたいが、うまい例えが思いつかない

exp

  • 「いつまでこのJWTは使えるの?」
  • 有効期限

nbf

  • 「いつからこのJWTは使えるの?」

iat

  • 「このJWTはいつ発行されたの?」

jti

  • JWT固有のID
  • コンサートチケットでいうと、チケットに振られた固有の番号。特定のチケットを無効にしたい時に使う

大体こんな感じ

その3:payloadに何を含めるか

  • ユーザーのIDを含めるのか、役割を含めるのか、など。
  • 要はトークン本体に何を含めるのか、という話
  • JWTのpayloadに秘匿性なんてないのでパスワードなど絶対に含めてはいけない
  • コンサートチケットに、電話番号やemail、パスワードなどそのまま書いてあったら怖いですよね、って話。

JWTサンプル

実際のJWTがこれ
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYXVkaWVuY2UiLCJhdWRpZW5jZTIiXSwiZXhwIjoxNjg4NzMwMTg2LCJpYXQiOjE2ODg3MjgzODYsImlzcyI6Imlzc3VlciIsImp0aSI6IjM5ODE0YzNkLTczMDUtNDgyNS1iOGMwLTUwYzA4MGE1ODBmOSIsIm5hbWUiOiJ5dXlhbiIsIm5iZiI6MTY4ODgxNDc4Niwicm9sZSI6ImFkbWluIiwic3ViIjoic2FtcGxlLWp3dC1zdWJqZWN0In0.fXiaAQctQiS91s9bi7B_lkoYU-I3HvlT5GLd6y3QJDg

header部分

# headerの中身
{
  "alg": "HS256",
  "typ": "JWT"
}

payload部分

# payloadの中身
{
  "aud": [
    "audience",
    "audience2"
  ],
  "exp": 1688730186,
  "iat": 1688728386,
  "iss": "issuer",
  "jti": "39814c3d-7305-4825-b8c0-50c080a580f9",
  "nbf": 1688814786,
  "sub": "sample-jwt-subject",
  
  "name": "yuyan",
  "role": "admin"
}

こんな感じ

JWTを使う上での注意点

  • JWTは検証しなくても中身はわかるのでpayloadにパスワードなど秘密情報を含めてはいけない。
  • JWTを個別に無効にするには別の工夫が必要。「ログアウトしたときにそのJWTを無効にしたい」っていうのはJWTだけで行うのは難しい(jtiを記憶するための何かが必要)

https://jwt.io/

https://openid-foundation-japan.github.io/draft-ietf-oauth-json-web-token-11.ja.html

Discussion