ゆるテックノート

JWTの中身が読めても、その情報を信用できるとは限らない

ログインの不具合を調べようとJWTをデコードしたら、ユーザーIDや有効期限が読めた。ここで気になるのが「読めるなら、書き換えられるのでは」という点です。文字を展開する処理と、改変されていないか・このサービスで受け付けてよいかを確かめる処理は、それぞれ役目が違います。

よく見かける3区分は、署名などを付けるJWS形式

ピリオドで区切られた3区分のコンパクト形式では、先頭がヘッダー、次がペイロード、最後が署名またはMACです。JWTのペイロードには、subやexpなどのクレーム(対象や条件についての値)が入ります。JWSのヘッダーとペイロードはBase64urlで表されるため、暗号化された秘密の文章ではありません。

JWSとJWEを同じものとして読まない

JWTには暗号化されたJWEもあり、そのコンパクト形式は5区分です。Online ToolsのJWTデコーダーは3区分の形式を読みます。JWEの復号や、すべてのJOSE拡張の処理を行うツールではありません。3区分であっても、デコード成功だけで署名が正しいとは言えません。

署名のない学習用データで中身を読む

次はこの説明用に作ったデータです。algはnoneで、署名はなく、期限は1970年です。ログインに使うためのトークンではなく、デコードだけなら保護のない内容でも読めることを確かめる例です。最後のピリオドも含めて入力します。

入力

eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJkZW1vIiwiaWF0IjowLCJleHAiOjYwfQ.

得られるヘッダーとペイロード

expの60は1970年1月1日00:01:00 UTC、日本時間では同日09:01:00です。デコーダーで期限切れと表示されても、署名を調べた結果ではありません。noneを認証用の受信側で無条件に許可してよいという例でもありません。

{"alg":"none","typ":"JWT"}

{"sub":"demo","iat":0,"exp":60}

署名検証は、許可した方式と信頼する鍵で行う

デコードは表記を戻すだけなので、ペイロードにadminと書かれていても、その権限を与える根拠にはなりません。署名またはMACの検証では、アプリが許可したアルゴリズムと信頼できる鍵を使い、署名対象の入力が一致するかを確かめます。トークンが指定したalgを、そのまま受け入れる方針にはしません。

検証を通った後にも、受け入れ条件がある

正しい鍵で検証できても、別のサービス向けのトークンだったり、別の用途で発行されたものだったりする場合があります。発行者・受信者・期限・トークンの種類を、そのアプリの約束と照合します。実装には、これらを扱う保守されたJWTライブラリを使い、文字列の分割だけで認証処理を組み立てないようにします。

exp・nbf・iatは時刻だが、役目は同じではない

NumericDateはUnix epochからの秒数です。JavaScriptのDateで使うミリ秒と取り違えると、1970年付近など意図しない時刻になります。数値を画面に表示する時差と、期限を判定する基準時刻も分けて考えます。

時刻のクレームを読む

これらはRFC 7519の基本仕様では任意のクレームです。実際の認証プロファイルが必須にする場合もあります。時計のずれを考慮する小さな猶予は仕様上認められますが、Online Toolsの表示は端末の現在時刻で判定し、サーバーの猶予や失効状態を再現しません。

名前 意味 基本の判定
exp 受け入れ可能な期限 現在時刻がexp以上なら期限切れ
nbf それより前は受け入れない時刻 現在時刻がnbf未満ならまだ使えない
iat 発行された時刻 これだけで有効期限にはならない

issとaudが読めることと、一致を検証したことは別

issは発行者、audは受け手を表します。同じ発行者が複数のサービスへトークンを出すこともあるため、発行者だけ合っていればよいとは限りません。audは一つの文字列のほか、複数の受け手を表す配列にもなります。

デコーダーでわからないこと

Online Toolsは署名・MAC、発行者、受信者を検証しません。失効したか、利用済みか、利用者が今もその操作を許可されているかも判断しません。JSON.parseを使う表示では重複クレームや極端に大きい数値の情報を失う場合があるため、解析結果をそのまま認証の根拠へ渡さないようにします。

表示の確認には学習用データ、認証にはアプリ側の検証

JWTデコーダーで上の例を展開すると、ヘッダー・ペイロードとUTC/日本時間の時刻を見られます。ここでわかるのは「何が書かれているか」です。実サービスで受け付けるかどうかは、そのサービスの検証処理と照合します。

関連記事と仕様

表記の違いはBase64urlとパディング、JSONの読み取り条件はJSONの構文と数値精度へ。構造はRFC 7515 §3.1、時刻とクレームはRFC 7519 §2と§4.1、検証時の注意はRFC 8725 §3が根拠です。