メインコンテンツへ移動

JWTをデコードする方法

トークンは、ブラウザ内で動作するデコーダーに貼り付けてください。アップロードを求めるデコーダーは決して使わないこと。ヘッダーとペイロードを読み、expを現在時刻と照合し、alg:noneは警報として扱います。デコードは読むことであって、検証ではありません——トークンが本物だと証明できるのは、発行者の鍵だけです。以下のステップはすべて、サイトのJWTデコーダー上でローカルに実行されます。

端末内で処理
ツールだけ使いたい方はJWTデコードへJSON Webトークンをデコードし、各クレームの意味を表示ツールを開く

JWTをデコードする方法とは

JSON Web Tokenは一見ただの壁紙に見えます——ドットでつながった3つのbase64セグメントです。だからこそ、じっと眺めるのではなく、手順を踏んで意図的にデコードする価値があります。中には、署名アルゴリズムを名乗るヘッダー、トークンの所有者と失効時刻を名乗るペイロード、そして(本物なら)両方を発行者に結びつける署名が入っています。最初の2つは誰でも読めます。3つ目は、鍵を持つ者にしか検証できません。

このページは、その「読む作業」の安全版です。5つのステップ、各ステップにチェックが付き、すべて自分のブラウザ内で実行されます。この区別が重要なのは、JWTがベアラー資格情報——持っている人がそのまま使える——だからです。有効なトークンを、サーバー側でデコードする見知らぬWebページに貼り付けることは、その資格情報をページの所有者に渡すことです。ローカルでデコードするツールに貼り付ければ、誰にも渡りません。そしてNetworkパネルを見れば、その主張を信じる代わりに、自分で確認できます。

最初のステップの前に、最初に、ひとつ押さえておいてほしいことがあります。デコーダーは、トークンが何と言っているかを示し、怪しく見える点に警告を付けられますが、トークンが本物かどうかは教えてくれません。真偽は署名の問題であり、署名の問題には発行者の鍵が必要です。最後のステップは、その検証が実際にはどこで行われるのか——そしてその答えが、たいていこのページではない理由——についてです。

5つのステップはすべて、サイトのJWTデコーダーで実行されます——貼り付けて読むだけで、すべてのクレームにその意味が付いて届きます。

3つのセグメントの全体の構造については、JWT構造のガイドが、実物を調べる前に、実例とともに各部分を解説しています。

手順

  1. トークンだけをコピーする——前後は一切含めない

    応答ヘッダー、localStorageのエントリ、調査中のリクエストのどこからであれ、3つのbase64セグメントだけを正確にコピーします。eyJで始まり、ドット区切りの3つ目のセグメントの最後の文字で終わる部分です。Bearer接頭辞、引用符、空白は取り除いてください。デコーダーはある程度は許容しますが、きれいなトークンを貼り付ければ、後続のすべてのチェックが曖昧になりません。

    確認:コピーしたものはeyJで始まり(eyJは「{"」のBase64です。JWTのヘッダーはすべてそうなります)、空白を含みません。この小さなチェックが、ツールの出番より前に、最もありがちな貼り付けミスを捉えてくれます。

  2. ローカルで動くデコーダーに貼り付ける

    JWTデコーダーを開き、貼り付けます。ヘッダーとペイロードは、タブの中で即座にデコードされます。アップロードはなく、Networkパネルにもトークンを運ぶリクエストは現れません。ここが、この手順の安全版と危険版が分かれる地点です。サーバー側のデコーダーはあなたのトークンを見てしまいます。ローカルのデコーダーは見ません。トークンがマシンの外に出ないからです。

    確認:2つのJSONオブジェクトが現れます。ヘッダーが先、ペイロードが下で、間にエラーはありません。無効なbase64や壊れたJSONが報告されるなら、コピーの時点でトークンが切れたか壊れたかです。ソースからコピーし直してください。

  3. ヘッダーを読む——アルゴリズムが脅威モデルを決める

    ヘッダーのalgは、トークンの署名方法を名乗ります。HS256なら共有秘密鍵、RS256やES256なら発行者だけが秘密側を持つ鍵ペア、noneなら未署名です。ツールはalg:noneを大きな警告として表示します。未署名のトークンは誰でも書ける主張であり、それを受け入れるのは単純なミスではなく、ポリシーの失敗だからです。algの値はここで控えてください。ステップ5で必要になります。

    確認:algの行には実際のアルゴリズムが表示され(よくあるのはHS256、RS256、ES256の3つ)、noneと表示されることはありません。もしnoneと出たら、そこで止めてください。そのトークンは自身について何も証明していません。発行者には伝える必要があります。

  4. ペイロードを読み、expを現在時刻と照合する

    ペイロードのクレームは、トークンの物語のすべてです。subは所有者を、issは作成者を、audは受け入れてよい相手を名乗り、expは失効の時刻です。ツールは、自分で変換しなければならない1735689600ではなく、実在の日時に変換して表示します。ツールは警告も上げます。期限切れのトークン、そもそもexpのないトークン、発行者の時計が進んでいるせいでnbfが未来にあるトークンです。

    確認:expの行には、判断に使える日時が表示されます——「4時間前に期限切れ」「あと11分で期限切れ」のような形です。警告リストも、目の前のものと一致します。expのないトークンは疑ってください。書面上は決して失効しないのですから。

  5. 検証は発行者側で行う——デコーダーではありません

    デコードは、本物かどうかについて何も証明しません。署名セグメントは鍵と照らして初めて確認でき、鍵は発行者のものです。実際には、検証は鍵の在り処——自分のバックエンド、APIゲートウェイ、IDプロバイダーのライブラリ——で行われます。ブラウザのタブではありません。サービスがトークンを拒否した理由をデバッグしているなら、そのサービスにデコード済みのクレームを持ち込んでください。過ぎ去ったexp、一致しないaud、鍵キャッシュがまだ取り込んでいないkid。大半の拒否はこのどれかで説明がつきます。

    確認:目の前のトークンについて、誰が検証すべきか(それを受け入れたサービス)、そのサービスが何を確認するか(発行者の鍵による署名確認に加えて、exp、nbf、aud)を言い表せますか。言えないなら、足りないピースは、JWT構造のガイドにあります。

デコードと検証は別の行為です

デコードはbase64とJSONのパース——つまり「読む」ことです。検証は暗号学的な操作——発行者だけが持つ鍵と照らし合わせて署名を確認することです。「有効」の緑バッジを表示するデコーダーがあれば、それは嘘をついています。発行者の秘密鍵を持てるはずがなく、公開鍵アルゴリズムの場合でも、確認は受け入れる側のサービスの仕事だからです。どの鍵を、どの受け手(aud)に適用するかを知っているのは、受け入れる側だけです。

だからこそ、このツールは有効性を主張せず、見えたものを説明します。アルゴリズム、クレーム、日付に直した期限、そして注意深い読み手が挙げるであろう警告です。判定を下す必要のある読み手——トークンを受け取るAPI——こそが、判定に必要な材料を持つ唯一の当事者です。

毎回読む価値のあるクレーム

登録済みクレームのうち4つが、話の大半を運びます。exp:トークンが失効する時刻——このページで最も有用な1行です。sub:トークンの対象(誰のものか)。通常はユーザーかサービスの識別子です。issとaud:誰が発行し、誰が受け入れてよいか。あるAPI向けに発行されたトークンは別のAPIに拒否されることがあり、その拒否側は誤動作しているのではなく、audを正しく読んでいるのです。

カスタムクレーム(roles、scopes、テナントID)もその横に並び、同じ方法でデコードされます。ただし、事実ではなく主張として扱ってください。その信頼性は背後の署名に見合うものであり、デコーダーは署名を確認していないのです。iat(発行時刻)とnbf(この時刻までは無効)が時系列のクレームを締めくくり、「トークンはまだ有効ではない」という種類のエラーを説明します。このエラーの原因は、ほとんどの場合、時計のずれです。

デコーダーが警告できること——サーバーにしかわからないこと

警告レイヤーは、ペイロードが表面上認めている欠陥を捉えます。alg:none、空の署名セグメント、過去に消えたexp、そもそもexpのないトークン、未来にあるnbf。これらは鍵なしで見える欠陥であり、トークンが「動いている」ように見える場合でも読む価値があります。期限切れのトークンを受け入れるシステムは、把握しておきたいシステムです。

どんなブラウザツールにもわからないこと。それは、署名が実際に検証を通るか、発行者がまだこのセッションを認識しているか、トークンが失効済みかです。その答えは、発行者の鍵と記録の奥にあります。デコーダーの誠実さとは、トークンを読むことと、トークンを信頼することの間に引かれた境界線なのです。

トークンが潜む場所——それがローカルでのデコードを支持する理由

JWTはAuthorizationヘッダー、ブラウザのlocalStorage、ログインリダイレクト後のURLフラグメント、アプリケーションログに潜んでいます。ファイルよりもずっと気軽にコピーされる場所ばかりです。それらのコピーは、失効するまで有効な資格情報であり続けます。だからこそ、この手順のデコード・ステップはローカルツールにこだわります。デコード済みペイロードが写ったスクリーンショット、チケット、チャットメッセージは誰でも読めますが、生のトークンそのものは、それを検証するはずのサービス以外を旅すべきではありません。

実務上の習慣は、そこから導かれます。ローカルでデコードする。共有する前に塗りつぶす(デバッグには通常、スクリーンショット内のペイロードで十分です)。デバッグのためにトークンを運ばなければならないときは、発行者が許容するはずのチャネルで運ぶ——公開のペーストサイトは論外です。

よくある質問

自分のJWTをこのデコーダーに貼り付けても安全ですか?

はい。デコードはブラウザ内で行われるからです。トークンはローカルのコードがパースし、どこへも送信されません。信じる必要すらありません。DevToolsを開き、Networkパネルを開き、トークンを貼り付けてみてください。それを運ぶリクエストが存在しないのがわかります。サイトのContent-Security-Policyは構造上、外向きの接続を禁じているため、バグがあってもトークンを持ち出すことすらできません。

デコードでトークンが有効かどうかはわかりますか?

わかりません。わかると謳うデコーダーは、あなたを誤解させています。有効性は署名の問題です。トークンは、発行者の鍵と照合し、さらにそれを受け取るサービスのexp、nbf、audのポリシーに照らして確認されなければなりません。デコーダーにできるのは、トークンが何と言っているかを見せ、表面の欠陥(期限切れ、未署名、expの欠落)に警告を付けることです。判定は、鍵を持つ検証者の仕事です。

alg:noneとは何ですか?なぜ危険なのですか?

ヘッダーが署名アルゴリズムを宣言していない、という意味です。署名セグメントは空であるべきで、トークンの中身は誰でも書ける主張にすぎません。HS256のようなアルゴリズムは、改ざんを検知できるようにこそ存在します。alg:noneはその保証を取り除きます。デコーダーがこれを大きな警告として表示するのは、この種のトークンを受け入れることが既知の脆弱性クラスであって、書式の好みではないからです。

アプリは動いているのに、トークンが期限切れと表示されるのはなぜですか?

アプリがトークンを更新したからです。短命のアクセストークンは意図的に失効し、通常はリフレッシュトークンで新しいものと裏側で交換されます。あなたがコピーしたのは、おそらくすでに引退した世代です。アプリが今まさに送っているトークンをデコードすれば、expは未来にあるはずです。未来になければ、それを受け入れているサービスが期限を確認していません。知っておく価値のあることです。

exp、iat、nbfは何の単位で表されますか?

1970年1月1日UTCからの秒数です。いわゆるUnix時間です。生のペイロードでは、1735689600のような10桁の数字として表示されるのはそのためです。デコーダーはそれぞれを実在の日時に変換し、現在との関係(「4時間前に期限切れ」「あと11分で期限切れ」)で表します。人が実際に動ける形は、こちらです。

デコード済みトークンのスクリーンショットを共有してもいいですか?

デコード済みのヘッダーとペイロードは、通常は問題ありません。もともと誰でも読めるように設計されており、クレームの写ったスクリーンショットでデバッグすることはよくあります。決して共有してはならないのは、生のトークンそのものです。ベアラー資格情報であり、期限が来るまで、持っている人が使えてしまいます。署名セグメントと、チケットに書きたくない類の識別子は塗りつぶし、完全なトークンはスクリーンショットに一切写さないでください。