JWTは暗号化されていない:データ漏洩につながる誤解の第1位

開発者が最初にJSON Web Tokens(JWT)に触れると、多くの場合危険な前提を抱きます:トークンは暗号化された意味不明な文字列のように見えるため、中身のデータも安全だと考えるのです。この誤解は世界中のアプリケーションで数えきれないデータ漏洩や個人情報の露出、深刻なセキュリティ脆弱性を引き起こしてきました。実際のところ、標準的なJWTは、敏感な情報を葉書に書いて郵便で送るのとほぼ同じくらい安全性が低いものであり、誰かに傍受されればすべての内容を読むことができます。
根本的な誤解
JSON Web Tokensは、現代のWebアプリケーションにおける認証のデファクトスタンダードとなっています。コンパクトで自己完結型、ユーザーや権限に関する情報を持ち運ぶことも可能です。しかし、JWTの便利さを支える特徴は、しばしば誤解を招きます。重要な誤解は、JWTの見た目にあります:長いランダムな文字列がドットで区切られているのです。この外観が安全だと誤認させ、開発者が敏感な情報をトークンのペイロードに直接保存してしまう原因となっています。
実際には、JWTはそれほど安全ではありません。標準的なJWTは、ヘッダー、ペイロード、署名の3つの部分から構成され、ドットで区切られています。署名は暗号学的署名による整合性の検証に使われますが、ヘッダーとペイロードはBase64URLエンコードされているだけで、暗号化はされていません。Base64エンコードはバイナリデータをASCII文字列に変換する可逆的な処理であり、セキュリティのための仕組みではありません。基本的なプログラミング知識があれば、数秒でJWTをデコード可能です。
Base64エンコードと暗号化の違い
なぜこれが重要なのか理解するには、エンコードと暗号化の違いを押さえる必要があります。Base64エンコードは、データを特定の文字セットに変換する単純な可逆処理です。これはデータの送信や保存の互換性を保つためのものであり、安全性を保証するものではありません。ほとんどのプログラミング言語には、Base64のエンコード・デコードを行う組み込み関数があります。パスワードやキー、秘密情報は不要です。
一方、暗号化は秘密鍵を用いてデータを変換し、鍵がなければ読めなくなるようにする暗号学的な処理です。真の暗号化は機密性を提供します。暗号化されたデータを傍受しても、復号鍵がなければ内容は読めません。HS256やRS256のようなアルゴリズムで署名されたJWTは暗号化されていません。署名はあくまで整合性の検証に使われるものであり、内容を隠すものではありません。
JWTの署名は一つの役割を持ちます:トークンが改ざんされていないことと、信頼できる発行者から発行されたことを証明することです。サーバーがJWTを作成するとき、エンコードされたヘッダーとペイロードに秘密鍵を用いてハッシュ化した署名を生成します。受信側はこれを再計算して正当性を検証します。ただし、この署名はペイロードの内容を隠すものではありません。誰でもペイロードを読むことができ、署名を無効にしない範囲で内容を変更可能です。
JWTをデコードするのはどれほど簡単か?
非常に簡単です。典型的なJWTを例に見てみましょう:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIxMjM0NTY3ODkwIiwiZW1haWwiOiJqb2huLmRvZUBleGFtcGxlLmNvbSIsInNzbiI6IjEyMy00NS02Nzg5Iiwic2FsYXJ5Ijo4NTAwMCwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
このトークンをデコードするのに特殊なツールやハッキングスキルは必要ありません。jwt.ioのようなデバッグサイトに貼り付けるだけで、すぐに中身が見えます。プログラミング言語を使えば、次の一行でデコード可能です:
JSON.parse(atob(token.split('.')[1]))
Pythonならこちら:
import base64, json
json.loads(base64.b64decode(token.split('.')[1] + '=='))
例のトークンのペイロードをデコードすると、次のようになります:
{
"userId": "1234567890",
"email": "john.doe@example.com",
"ssn": "123-45-6789",
"salary": 85000,
"iat": 1516239022
}
この例は、重大なセキュリティの失敗を示しています。トークンには、社会保障番号や給与情報など、絶対に平文で送信すべきでない個人情報が含まれています。しかし、実際の運用システムではこのようなケースがセキュリティコミュニティが認めた以上に頻繁に起きています。
実世界の影響:JWTの誤用によるデータ漏洩
JWTを暗号化されたコンテナと誤認することの結果は、理論的なものではありません。セキュリティ研究者やペネトレーションテスターは、JWTペイロードから敏感な情報が漏洩しているアプリケーションを定期的に発見しています。一般的な例は以下の通りです:
医療アプリケーション:患者の医療記録番号や診断コード、保険情報をJWTに保存。攻撃者がこれらのトークンを傍受すれば、データベースに侵入せずとも保護された医療情報にアクセス可能。
金融サービスプラットフォーム:口座残高や取引履歴、内部ユーザーIDなどを含むトークン。ネットワーク傍受やクライアントサイドのJavaScriptアクセスを通じて、金融データが直接露出します。
Eコマースサイト:クレジットカードの下4桁や配送先住所、購入履歴をトークンに埋め込み。部分的なカード番号は安全だと思われがちですが、他の漏洩情報と組み合わせると、なりすましやアイデンティティ盗難に繋がります。
内部企業アプリケーション:社員IDや給与範囲、パフォーマンス評価、組織の階層情報をJWTに格納。ブラウザの開発者ツールやネットワークログから漏洩した場合、機密情報が露出します。
2025年の最近の脆弱性(例:CVE-2025-2079やCVE-2025-20188)では、ハードコーディングされたJWTシークレットにより攻撃者が有効なトークンを生成できる問題がありましたが、根本的な問題はシークレット管理だけにとどまりません。適切に署名シークレットを保護していても、JWTのペイロードが読み取れる性質は、敏感情報を保存する際のリスクを内在させています。
JWTの構造:何が実際に保護されているのか?
JWTは3つの構成要素からなります。それぞれが特定の役割を持ちます:
ヘッダー(Base64URLエンコード):トークンの種類と署名アルゴリズムを指定します。例:
{
"alg": "HS256",
"typ": "JWT"
}
ペイロード(Base64URLエンコード):クレーム(主張)や追加情報を含みます。ここに敏感な情報を保存しがちですが、誤った使い方です。
署名(暗号学的に生成):トークンの整合性を検証します。HS256の場合、エンコードされたヘッダーとペイロードに秘密鍵を用いてHMAC-SHA256でハッシュ化します。
署名だけがセキュリティの役割を持ち、その性質は整合性の保証です。内容の秘密性はありません。署名はトークンが改ざんされていないことと、秘密鍵を持つ発行者による発行を証明しますが、ペイロードの内容を隠すものではありません。
JWTに本当に入れるべきものは?
敏感な情報はJWTのペイロードに入れるべきではありません。では何を入れるべきか?サーバーがリクエストを検証し、ステートレス認証を維持するための非敏感な識別子やメタデータです。
適切なJWTペイロードの内容例:
- ユーザーIDやユーザー名(非敏感な識別子)
- トークンの有効期限(expクレーム)
- 発行日時(iatクレーム)
- 発行者(issクレーム)
- サブジェクト(subクレーム)
- 対象者(audクレーム)
- 非敏感なユーザーロールや権限
- セッションID
ポイントは、ペイロードに入れる情報は、アプリケーションのログやサーバーのコンソールに表示しても問題ない範囲のものであることです。もし、その情報がログファイルに記録され、他者に読まれる可能性があるなら、JWTペイロードに入れるべきではありません。
敏感情報については、サーバー側で管理し、JWTには参照用のIDだけを格納します。サーバーはトークンを受け取ると、ユーザーIDやセッションIDを抽出し、セキュアなデータベースから追加情報を取得します。この方法で、ステートレス認証の利点を維持しつつ、敏感情報を保護します。
代替案:セッション管理におけるOpaque Tokens
多くのセキュリティ専門家は、セッション管理にはJWTよりもOpaque Tokensの方が安全性が高いと主張します。Opaque Tokenはランダムに生成された文字列で、サーバー側のセッションデータの検索キーとして機能します。JWTとは異なり、Opaque Tokenはユーザーやセッションについて何も示しません—傍受されても意味を持ちません。
Opaque Tokenを実装する際は、サーバーが暗号的にランダムな文字列を生成し、それをセッションデータと紐付けて安全なストレージ(RedisやDB)に保存します。クライアントにはこのトークンを返し、その後のリクエストでトークンを送信させ、サーバーは対応するセッション情報を検索します。トークンが存在しない、または期限切れの場合はリクエストを拒否します。
この方法のセキュリティ上の利点は、トークンの中身が公開されないことです。中身はランダムな識別子だけです。さらに、サーバー側の記録を削除すれば、セッションを即座に取り消せます。ステートレスJWTでは不可能なことです。第三に、敏感なセッションデータはサーバー環境から一切出ません。
ただし、サーバー側のストレージ容量やリクエストごとのDBアクセスが増える点はトレードオフです。多くのアプリケーション、特に敏感なデータを扱う場合は、このトレードオフは価値があります。セキュリティの観点からは十分なメリットです。
JWTが適しているケース
これらの懸念にもかかわらず、JWTには正当な用途があります。分散システム間のステートレス認証や、マイクロサービスアーキテクチャのように中央のセッションストレージがボトルネックになる場面で特に有効です。
API認証では、トークンに非敏感な識別子だけを含め、APIゲートウェイが署名を検証できる場合に適しています。シングルサインオン(SSO)システムでは、複数のアプリケーションが同じ発行者を信頼し、ユーザー認証を独立して検証するのに役立ちます。OAuth 2.0の短期アクセス・トークンでは、有効期限が数分単位で設定され、追加の検証が必要な場合に適しています。
ポイントは、JWTは認証と整合性の検証には優れていますが、機密性は保証しません。適切にペイロードを最小限にし、セキュリティコントロールを適用すれば、その役割を十分果たします。誤解により、JWTを暗号化されたコンテナと誤認することが問題です。
JWE:本当に暗号化が必要な場合
暗号化されたトークンペイロードが必要な場合、JWT仕様にはあまり知られていない兄弟分:JSON Web Encryption(JWE)があります。JWEは実際に暗号化されており、ペイロードの機密性を確保します。標準的なJWT(JWS:JSON Web Signature)とは異なり、JWEは暗号化キーなしではデコードできません。
JWEは5つの部分から構成され、ヘッダー(暗号化アルゴリズムやキー管理)、暗号化されたキー、初期化ベクター、暗号文、認証タグを含みます。ペイロードはAES-GCMなどのアルゴリズムを用いて本当に暗号化されており、復号鍵がなければ読めません。
ただし、JWEは複雑さを増します。鍵管理が重要になり、鍵を失うとすべての暗号化されたトークンにアクセスできなくなります。暗号化・復号の処理によるパフォーマンスのオーバーヘッドも増加します。暗号化コードの実装ミスは、平文の保存よりも脆弱性を生む可能性があります。
ほとんどのアプリケーションではJWEは必要ありません。敏感なデータはトークンに入れず、サーバー側のストレージと参照IDを使うのが一般的です。特定のシナリオ、例えば異なる信頼境界を持つ組織間で認証情報を共有する場合にJWEを検討します。
JWTのセキュリティのベストプラクティス
安全なJWT認証を実現するには、以下のベストプラクティスに従う必要があります:
敏感なデータはJWTペイロードに保存しない。 これを最も重要なルールとして繰り返します。JWTは攻撃者に読まれる可能性があると想定し、すべてのJWTを危険なものとみなすべきです。
短い有効期限を設定する。 数分や数時間に設定し、長期間の有効期限は避ける。これにより、漏洩したトークンの被害を限定できます。
トークンリフレッシュを実装する。 短期のアクセストークンと長期のリフレッシュトークンを併用し、安全性と利便性を両立させます。
すべてのJWTクレームを検証する。 署名の検証、期限の確認、発行者や対象者の検証を必ず行います。未検証のJWTは信用しないこと。
強力な署名アルゴリズムを使用する。 RS256(RSA署名)をHS256(HMAC)より優先し、アルゴリズムの混乱攻撃を防ぐために、サーバー側で期待するアルゴリズムを厳格に設定します。
署名秘密鍵を安全に保管する。 コードにハードコーディングしたり、バージョン管理にコミットしたりしない。環境変数や秘密管理サービスを利用します。
適切なHTTPSを使用する。 JWTは暗号化された通信経由で送信し、盗聴や改ざんを防ぎます。
トークン失効の戦略を検討する。 JWTはステートレスですが、重要なシステムではブラックリストや最小限のセッション管理を行い、失効を可能にします。
開発チームへの教育
最も重要なセキュリティ対策は教育です。JWTの誤解は、エンコード、署名、暗号化の違いを理解していないことに起因します。セキュリティ研修では、このギャップを埋める内容を含め、JWTがどれほど簡単にデコードできるかを示し、その影響を強調すべきです。
コードレビューでは、敏感な情報をペイロードに保存しているJWTの実装を指摘し、静的解析ツールも誤用パターンを検出できるよう設定します。セキュリティテストには、JWTの内容に敏感情報が含まれていないかを確認することも含めます。
組織としては、JWTの使用に関する明確なポリシーを策定し、どのデータをトークンに入れるべきか、何をサーバー側で管理すべきかを規定します。ドキュメントには正しい例と誤った例を掲載し、そのセキュリティ上の影響を明示します。
結論
JWTが暗号化を提供しているという誤解は、多くのデータ漏洩を招き続けており、システムのリスクを高めています。Base64エンコードは暗号化ではないことを理解することが、セキュアな認証システムの構築に不可欠です。標準的なJWTは署名による整合性検証を提供しますが、ペイロードの内容を秘密にするものではありません。
安全なアプリケーション開発のためには、JWTのペイロードは公開されても問題ない情報だけにし、敏感な情報はサーバー側で管理し、必要に応じてOpaque Tokensを検討してください。JWTを使う際は、ベストプラクティスに従い、すべてのクレームを検証し、有効期限を適切に設定しましょう。
現代Webアプリケーションのセキュリティは、開発者が使うツールの理解にかかっています。JWTは正しく使えば強力で便利ですが、誤解すれば危険です。JWTは暗号化されていないことを認識し、それに応じた設計を行うことで、利点を活かしつつ、セキュリティリスクを回避できます。次回JWT認証を導入する際は、「サーバーログに出したくない情報はJWTペイロードに入れるべきではない」と心に留めてください。ユーザーのプライバシーとアプリの安全性は、これを正しく理解することにかかっています。
Related InstaTunnel pages
Continue from this article into the most relevant product guides and workflows.
Related Topics
Keep building with InstaTunnel
Read the docs for implementation details or compare plans before you ship.