0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【セキュリティ入門】第5回 ID Token・Access Token・Refresh Tokenの違い

0
Posted at

はじめに

そもそも「トークン」とは?

ログインの仕組みを考えるとき、まず「トークン」という言葉が何を指すのかを押さえておきましょう。

用語メモ:トークン(token)とは?
ここでいうトークンは、ログインに成功したことを証明する**電子的な「引換券」や「入館証」**のようなものです。
窓口(サーバー)でログインすると、代わりにこの券を渡され、以降はその券を見せることで「私はさっきログインした本人です」と伝えられます。
毎回パスワードを見せなくてよくするための仕組み、とイメージしてください。

このシリーズの第4回では、その券の中身が「JWT(ジョット)」という形式で書かれていることを学びました。今回はその券が3種類あるという話です。

用語メモ:JWT(JSON Web Token)とは?
トークンの中身を書くための決まった書式のひとつ。
中身は暗号化されておらず、誰でも読める(ただし勝手に書き換えると「署名」で見破られる)というのが第4回のポイントでした。
「読めるけど改ざんはできない券」と覚えておけばOKです。

この記事でわかること

  • 3種類のトークンがそれぞれ「何を証明する券」なのか
  • ID トークンで API を守ってはいけない理由
  • リフレッシュトークンで「ログインし直さずに済む」仕組みと、その限界
  • ログアウトしたのにトークンが使えてしまう理由

この記事の前提

第3回(OIDC)と第4回(JWT)を読んでいると理解しやすいですが、読んでいなくても大丈夫なように、必要な用語はその都度「用語メモ」で補います。

本編

登場人物を先に整理する

トークンの話は「誰が誰に何を渡すか」が分かりづらいので、先に登場人物を日常語で押さえます。

用語メモ:3人の登場人物

  • クライアント … あなたが使うアプリ本体(スマホアプリ、ブラウザで動くWebアプリなど)。券を受け取る人。
  • 認可サーバー(Authorization Server) … 券を発行する窓口。AWS では Cognito がこの役目をします。
  • API(Resource Server) … 券を見せて使うサービスの裏側(例:注文一覧を返すサーバー)。券を見て「この人に見せてよいか」を判断する人。

以降、この「アプリ本体」「窓口(Cognito)」「API」の3人が出てきます。

なぜトークンは3種類もあるのか

「1種類の券で全部済ませればラクなのに、なぜ3つに分けるの?」という素朴な疑問から入ると分かりやすいです。分ける理由は主に3つあります。

  1. 「誰か」と「何をしてよいか」は別の話だから
    「あなたが田中さん本人だ」ということと、「田中さんは注文を見てよい」ということは、別々の情報です。これを1枚の券に混ぜると境界が曖昧になります。

    用語メモ:認証(AuthN)と認可(AuthZ)

    • 認証(にんしょう) … 「あなたは誰か?」を確かめること。ログインそのもの。
    • 認可(にんか) … 「あなたは何をしてよいか?」を判断すること。権限のチェック。
      名前が似ていて紛らわしいですが、「認証=誰か」「認可=何をしてよいか」とセットで覚えると混乱しません。
  2. 持たせたい寿命が正反対だから
    後で詳しく書きますが、ある券は「盗まれたときの被害を小さくしたいのですぐ切れてほしい」、別の券は「何度もログインさせたくないので長持ちしてほしい」。要求が真逆なので、1枚にまとめると両立できません。

  3. 渡す相手が違うから
    ある券はアプリ本体が自分で読むだけ、ある券は API に渡す、ある券は発行元(Cognito)にしか渡してはいけない。渡す相手が違えば扱い方も変わるので、混ぜないほうが安全です。

3つのトークンの役割(まず全体像)

細かい話に入る前に、3種類を1枚の表で見比べます。ここは「へぇ、こう違うんだ」と眺めるだけでOKです。

ID トークン アクセストークン リフレッシュトークン
ひとことで言うと 誰がログインしたかの証明 何をしてよいかの証明 券を再発行してもらう権利
主な渡し先 アプリ本体(自分で読む) API 窓口(Cognito)だけ
寿命 短い 短い 長い
誰がチェックする アプリ本体 API 側 発行元だけ

次の図で「どの券をどこに渡すか」を見ると、渡し先の違いが一目で分かります。

who-gets-which-token.png

ポイント:
アクセストークンだけが API に渡ります。
ID トークンとリフレッシュトークンは API には渡しません。
この違いがこの記事のいちばん大事なところです。

ID トークン — 「誰がログインしたか」の証明

ID トークンは、アプリ本体が「ログインできた」「この人は誰か」を知るための券です。

  • ユーザー名やアイコンを画面に表示する、といった用途に使います。
  • API に見せて「入っていいか」を判断させる用途では使いません(理由は次で説明します)。

中身の例を見てみましょう(Cognito の場合)。今は細部を暗記する必要はなく、メールアドレスなど「その人の情報」が入っている点だけ見てください。

{
  "sub": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
  "cognito:username": "my-test-user",
  "email": "my-test-user@example.com",
  "aud": "xxxxxxxxxxxxexample",
  "token_use": "id",
  "exp": 1676316377
}

用語メモ:token_use とは?
Cognito の券に入っている「この券の種類」を表す印です。
ID トークンなら id、アクセストークンなら access と書かれています。
あとで「券を取り違えていないか」を確かめるときの目印になります。

用語メモ:aud(audience)とは?
「この券は誰宛て?」を表す欄です。ID トークンの aud にはアプリ(クライアント)のIDが入ります。
つまり ID トークンはアプリ本体宛ての券であって、API 宛てではない、ということです。

アクセストークン — 「何をしてよいか」の証明

アクセストークンは、API に「私はこれをしてよい権限を持っている」と示すための券です。API へのリクエストのときに、次のように付けて送ります。

GET /orders HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJraWQi... (アクセストークン)

中身には、その人の属性(メールなど)ではなく、権限に関する情報が入ります。

{
  "sub": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
  "cognito:groups": ["testgroup"],
  "client_id": "xxxxxxxxxxxxexample",
  "token_use": "access",
  "scope": "phone openid profile email",
  "exp": 1676317451
}

ID トークンには email があったのに、アクセストークンには(Cognito では標準では)入っていない点に注目してください。
ID は「誰か」、アクセスは「何をしてよいか」 を答える券なので、載っている情報が違うのです。

用語メモ:Bearer トークンとは?
Authorization: Bearer ... の「Bearer(ベアラー)」は「持参人」という意味。
その券を持っている人なら誰でも通してしまう方式です。
改札の切符と同じで、落とした切符を拾った人でも改札を通れてしまう、というイメージ。
だから「盗まれたら悪用される=すぐ切れるようにしておく(短命化)」が最大の防御になります。

なぜ ID トークンで API を守ってはいけないのか

ここが最初のつまずきポイント。

  • ID トークンには 「何をしてよいか」の情報(権限)が入っていません。API はそれを見ても許可の判断ができません。
  • ID トークンの aud(宛先)はアプリ本体であって API ではありません。つまり「API 宛てではない券」を無理やり API に見せている状態になります。

たとえるなら、社員証(あなたが誰かを示す)を「立ち入り許可証」の代わりに使おうとするようなものです。
社員証で本人確認はできても、「サーバー室に入ってよい」という許可は別の証明が必要です。
API を守るのは、必ずアクセストークンの役目です。

リフレッシュトークン — 「券を再発行してもらう権利」

アクセストークンはすぐ切れます。切れるたびにログインし直すのは面倒です。そこで登場するのがリフレッシュトークンです。

リフレッシュトークンは、**アクセストークンが切れたときに、パスワードを入れ直さずに新しい券をもらうための「引換券」**です。

  • 長持ちする券なので、保管の慎重さがいちばん求められます(盗まれると長い間悪用されうる)。
  • ローテーションという仕組みが推奨されます。

用語メモ:ローテーションとは?
券を更新するたびに、古いリフレッシュトークンを無効にして新しいものに差し替えること。もし古い券が盗まれて使われても、「もう無効なはずの券が使われた」と気づいて遮断できます。使い捨ての整理券を毎回引き直すイメージです。

新しい券をもらうときのやり取りはこんな形です(今は眺めるだけでOK)。

POST /oauth2/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&client_id=xxxxxxxxxxxxexample&refresh_token=eyJjdHki...
{
  "access_token": "eyJraWQiOi...",
  "id_token": "eyJraWQiOi...",
  "token_type": "Bearer",
  "expires_in": 3600
}

ローテーションが有効なときは、この応答に新しいリフレッシュトークンも一緒に入っていて、使った古い券は無効になります。

大事な注意:リフレッシュトークンがあっても「永久にログイン状態」ではありません。 これにも最大寿命があり、そこを超えれば結局ログインし直しになります。

3つの券がどう連携するか(流れで見る)

「切れたら更新する」という流れを時間順に追うと、3つの券の連携が見えてきます。

用語メモ:401 とは?
API が「その券では通せません(認証・トークンに問題あり)」と返すときのエラー番号です。券が切れていたり、券を取り違えていたりすると出ます。

Cognito での具体的な寿命(数字は眺めるだけでOK)

「短い」「長い」だけだとイメージしづらいので、AWS の Cognito での実際のデフォルト値を挙げます。細かい範囲は今は暗記不要です。

券 デフォルトの寿命 設定できる範囲
ID トークン/アクセストークン 1時間 5分〜1日
リフレッシュトークン 30日 60分〜10年

有効期限はどう決める?

「短ければ短いほど安全」ではありません。
安全性と使いやすさ(UX)のバランスで決めます。

観点 考え方
アクセストークン 短くすると、盗まれても悪用できる時間が短くなる。数分〜1時間が一般的
リフレッシュトークン 「どのくらいの頻度なら再ログインを我慢してもらえるか」で決める
短くしすぎる副作用 更新の頼みごとが増え、窓口(Cognito)への通信が増えて重くなる

つまり、アクセストークンは「盗まれたときのリスク」から、リフレッシュトークンは「使いやすさ」から決める、と考える軸が分かれます。

ログアウトしたのにトークンが使えるのはなぜ?

JWT でいちばん誤解されるところです。

「ログアウトしたんだから、もう無効でしょ?」と思いますよね。ところが、そうとは限りません。

理由:JWT は自己完結した券です。署名が正しくて、まだ期限(exp)が切れていなければ、わざわざ発行元(Cognito)に問い合わせなくても「有効」と判断されます。だから、アプリが手元の券を捨てても、そのコピーがどこかに残っていれば、期限が切れるまで使えてしまうのです。

たとえ:一度発行した「入館証」を回収せずに期限だけ書いておくと、本人が「もう使いません」と言っても、コピーを持っている人は期限までゲートを通れてしまう、というイメージです。

現実的な対策は、次の順で考えます。

  1. アクセストークンを短命にする(いちばん現実的。悪用できる時間=有効期限に抑える)。
  2. リフレッシュトークンを無効化(revoke)する(新しいアクセストークンを取れなくして、更新の道を断つ)。
  3. どうしても即座に無効化したい場合は、券を1枚ずつ問い合わせて確認する仕組みを入れる。ただし、これは JWT の「自己完結で速い」という長所を捨てることになり、コストがかかります。

用語メモ:revoke(リボーク)とは?
券を「無効にする(取り消す)」こと。とくにリフレッシュトークンを revoke すると、以降は新しいアクセストークンを取り直せなくなります。

401 が出たときの調べ方(トラブル時)

券の使い分けを間違えると、たいていは API 側の 401 として現れます。どこから疑うかの順番を図にしました。初級者はまず「一番上の分岐」だけ覚えれば十分です。

  • 最初に見るのは token_use。 ID トークンを API に送ってしまう事故が、いちばん多いです。
  • 次に exp(切れていれば更新で解決)。それでもダメなら鍵や権限の問題を疑います。

よくある誤解

よくある誤解 実際は
「ID トークンで API を守れる」 守れません。権限(scope)を持たず、宛先(aud)もアプリ向けだからです。API を守るのは必ずアクセストークン。
「アクセストークンにメールアドレスが入っているはず」 Cognito では標準では入りません。属性が必要なら ID トークンなどを使います。
「リフレッシュトークンがあれば永久にログイン状態」 最大寿命があります。更新しても寿命は延びません。
「ログアウト=トークン無効化」 手元で券を捨てるだけでは無効になりません。期限が切れるか、revoke するまで有効です。

まとめ

観点 押さえるポイント
3つの役割 ID = 誰かの証明、アクセス = 何をしてよいかの証明、リフレッシュ = 再発行の権利。 渡す相手も、チェックする人も違う
API の保護 API を守るのは必ずアクセストークン。 ID トークンは API に送らない
ログアウト すぐ無効にならないのは、JWT が「自己完結した券」だから。現実解は「短命化 + リフレッシュの revoke」
デフォルト寿命 Cognito では ID/アクセス = 1時間、リフレッシュ = 30日

参考リンク

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?