JWTトークンデコーダー
JSON Web Tokenをデコードして内容を確認します。
Decoding only — the signature is not verified. Nothing is sent anywhere.
概要
JWT はドットで区切られた base64url の 3 つの部分です。署名方法を記したヘッダ、 クレームのペイロード、そして前 2 つに対する署名です。このエンコードは暗号化では ありません。トークンを持っている者は誰でも中のすべてのクレームを読めます。 ペイロードに何かを入れる前に、この点を頭に入れておく価値があります。
このツールは最初の 2 つの部分をデコードして整形し、3 つの時刻クレームを取り出して Unix 秒から読める日時に変換します。署名は検証しません。それが機能の欠落では なく正しい判断である理由は下記のとおりです。
使い方
- トークンを貼ります。
Bearerより後ろの部分です(スキーム自体は JWT の 一部ではありません)。 - header でアルゴリズムと、あれば鍵 ID を確認します。
- payload でクレームを読みます。
iat・nbf・expと失効判定の表を確認します。
登録済みクレーム
| クレーム | 名称 | 補足 |
|---|---|---|
iss | 発行者 | 誰が発行したか。サーバー側で許可リストと照合すべき値 |
sub | 主体 | 誰についてのトークンか。通常はメールではなく安定した利用者 ID |
aud | 受け手 | 誰向けのトークンか。別の受け手向けのトークンは拒否しなければならない |
exp | 失効時刻 | Unix 秒。これを過ぎたら拒否 |
nbf | 有効開始 | Unix 秒。これより前は拒否 |
iat | 発行時刻 | Unix 秒。「N 時間より古ければ再認証」に使える |
jti | JWT ID | 一意な ID。失効管理やリプレイ検査に使う |
7 つすべて仕様上は任意です。exp を持たないトークンは自力では失効しません。
そういうトークンを見たときに気づく価値のある設計判断です。
署名検証を提供しない理由
署名の検証には鍵が必要です。HS256 なら共有秘密、RS256 と ES256 なら公開鍵です。
対称鍵の場合、検証する鍵は署名する鍵と同一です。つまり検証機能を提供する
ページは、「あなたのシステム向けのトークンを誰でも発行できてしまう資格情報」を
貼るよう求めることになります。これを安全に作る方法は存在しません。
それでもデコードには価値があります。JWT のデバッグの大半は「このトークンは
実際に何と言っているのか」です。受け手が違う、スコープが無い、exp が 5 分前、
sub がコードの想定する UUID ではなくメールアドレス。これらはすべて鍵なしで
見えます。
デコードで分からないのは、そのトークンが本物かどうかです。受け取った JWT は、 サーバーが信頼する鍵で署名を検証するまでは、事実ではなく主張です。
alg は検証側が委ねてよい判断ではない
ヘッダの alg は送信側が使ったものを記述しています。alg を読んでそれに
応じてアルゴリズムを選ぶ検証実装は、攻撃者が制御する入力を信用していることに
なり、古典的な破り方が 2 つ生まれます。
alg: none。unsecured JWT は仕様上正当です。これを尊重する検証実装は、 空の署名を付けた任意のペイロードを受け入れます。RS256をHS256に差し替える。ライブラリが公開鍵を HMAC の秘密鍵として 使ってしまう場合、公開鍵を知る攻撃者(公開されています)は、こちらが受け入れる トークンを署名できます。
どちらも暗号の問題ではなく設定の問題です。期待するアルゴリズムを検証側で固定し、 それ以外を拒否してください。トークンから読み取ってはいけません。
ブラウザでトークンをどこに置くか
トークンをデコードして中身が読めることに気づいた人が、必ず次に尋ねる点です。 短く言えば、JWT はベアラ資格情報なので、それを読めるものはそれを使えます。
localStorage は同一オリジンの任意のスクリプトから読めるので、XSS が起きれば
トークンも持っていかれます。一方で自動送信はされないので、それ経由で CSRF される
ことはありません。ある種類の不具合を別の種類と交換しているだけです。
HttpOnly な Cookie はスクリプトから一切読めないので、XSS では盗めません。
ただし自動送信されるので、SameSite=Lax または Strict と Secure が必要で、
状態を変えるエンドポイントには独自の CSRF 対策が必要です。
ブラウザアプリでの通例の結論は、寿命の短いアクセストークンをメモリだけに保持し、
HttpOnly / Secure / SameSite なリフレッシュトークン Cookie から更新する形です。
これなら XSS が取れるのは数分の寿命のトークンで、長寿命の秘密は JavaScript から
到達できません。
どれを選んでも、実際に効いているのは exp です。失効リストを持たない 1 時間の
トークンは、盗まれてから 1 時間有効で、クライアント側の工夫で変わるものは
何もありません。
使用例
- 説明できない 401 — デコードして
audを確認します。ある API 向けに発行した トークンを別の API に提示するのが最も一般的な原因で、エラーメッセージが そう言うことはまずありません。 - 断続的な 401 —
expとiatを比べます。短い寿命とトークンをキャッシュする クライアントの組み合わせは、構造上断続的になります。 - 通るはずの権限が通らない — ペイロードの
scopeかrolesを見ます。 クレームが無ければ、問題は認可コードではなく発行側にあります。 - トークンに何を入れているかの棚卸し — 自社のトークンをデコードします。 メールアドレス・内部 ID・機能フラグなど、そこにあるものはすべて、 トークンを持つ者(保存先のブラウザを含む)から読めます。
注意事項
デコード前にパディングを補います。base64url は通常パディングを省くためです。 ログからコピーしたトークンはパディングが付いていることもあり、どちらの形でも 動きます。
失効判定はローカルの時計を使うので、権威ではなく便宜です。特定の時点と
比較したい場合は、生の exp の値を
Unix タイムスタンプ変換 にかけてください。
ペイロードは原理上は常に JSON とは限りません(仕様は任意のコンテンツタイプを 許します)が、実際の JWT はすべて JSON オブジェクトを運んでおり、JSON として 解析できないペイロードは隠さずに生のテキストとして表示します。