JWTはモダンなWebアプリケーションの認証基盤として広く普及しています。しかし「とりあえず動いている」状態で使われることも多く、セキュリティ上の落とし穴を踏んでしまうケースも少なくありません。この記事では、JWTの内部構造から安全な実装の判断基準まで、実務で必要な知識を体系的に解説します。
JWTの3つの構成要素
JWTは `ヘッダー.ペイロード.署名` の3部構成で、それぞれBase64URLエンコードされドットで連結されます。ヘッダーには署名アルゴリズム(`alg`)とトークン種別(`typ: "JWT"`)が含まれます。ペイロードにはクレームと呼ばれる情報が入り、`sub`(サブジェクト=ユーザーID)、`exp`(有効期限のUnixタイムスタンプ)、`iat`(発行時刻)などの標準クレームと、アプリケーション固有のカスタムクレームを含めます。署名はヘッダーとペイロードを秘密鍵またはキーペアで署名したもので、トークンの改ざんを検出するために使われます。重要なのは、ペイロードはBase64URLエンコードされているだけで暗号化されておらず、誰でもデコードして中身を読めることです。機密情報はペイロードに含めてはいけません。
HS256とRS256:署名アルゴリズムの選択
HS256(HMAC-SHA256)は共通秘密鍵を使った対称署名です。シンプルで高速ですが、署名の検証にも同じ秘密鍵が必要なため、マイクロサービスなど複数サービスでトークンを検証したい場合は秘密鍵を全サービスに配布しなければならず、鍵の漏洩リスクが上がります。RS256(RSA-SHA256)は非対称鍵を使った署名です。秘密鍵(Private Key)で署名し、公開鍵(Public Key)で検証します。公開鍵は誰でも使えるため、認証サーバー以外のサービスも安全に署名を検証できます。マイクロサービスアーキテクチャや、外部サービスにトークン検証を委ねる場合はRS256が推奨されます。HS256はシンプルなモノリシックアプリケーションで有効です。
セキュアな実装のための5つの原則
第1に、`alg: "none"` を許可しないことです。一部のJWTライブラリは署名なしのトークンを受け入れてしまうため、検証時にアルゴリズムを明示的に指定することが必須です。第2に、有効期限(`exp`)を必ず設定し、アクセストークンは15分〜1時間程度の短い有効期限にします。第3に、リフレッシュトークンを別途管理し、アクセストークンが失効したらリフレッシュトークンで再発行する仕組みを実装します。第4に、ペイロードにパスワードや個人情報など機密データを含めないことです。第5に、JWTの保存場所はlocalStorageではなくHttpOnly Cookieを推奨します。localStorageはXSSで盗まれるリスクがありますが、HttpOnly CookieはJavaScriptからアクセスできないため安全性が高まります。
JWT Decoderで内容を検証する
JWT Decoderを使うと、実際のトークンのヘッダー・ペイロードをデコードして確認できます。デバッグ時に現在ログインしているユーザーのクレームを確認したり、有効期限が正しく設定されているかをチェックするのに便利です。JWT Builderでは独自のクレームとアルゴリズムを指定してトークンを生成でき、認証フローのテストに活用できます。ただし、本番環境の秘密鍵はデバッグツールに入力しないことが鉄則です。テスト用の鍵を用意してテスト専用トークンで検証する習慣をつけましょう。
まとめ
JWTは正しく実装すれば強力な認証メカニズムですが、アルゴリズムの許可リスト管理・有効期限の設定・保存場所の選択・機密情報のペイロード除外という4つの原則を守ることが安全性の前提です。既存の実装を見直す際は、JWT Decoderでトークンをデコードして現在のクレーム構成を確認することから始めましょう。