JWTのベストプラクティス!脆弱性を生まないためのセキュアコーディング術

2025年4月30日水曜日

セキュアコーディング

この記事では、JWTのベストプラクティスについて、初心者の方にも分かりやすく解説していきます!

JWTって認証でよく聞くけど、どう使えば安全なの?と疑問に思っている人もいるかもしれませんね。設定を間違えると、結構怖い脆弱性につながることもあるんです。

この記事を読めば、JWTをセキュアに使うための具体的な方法が分かりますよ。ちょっとした工夫で、ぐっと安全性が高まります。さあ、一緒にJWTの安全な使い方をマスターしましょう!

この記事で学べること

  • JWTの基本的な仕組みと、なぜベストプラクティスが大事なのか。
  • 安全な署名アルゴリズムの選び方と、超重要な鍵の管理方法。
  • トークンの有効期限設定や、厳格な検証方法といった実装のコツ。
  • ついついやってしまいがちな、JWTの危ない使い方とその対策。

JWTとは何か?なぜベストプラクティスが重要なのか

まず、JWT(JSON Web Token - ジェイソンウェブトークンと読みます)って何か、簡単におさらいしましょう。

JWTは、インターネット上で情報を安全にやり取りするための一つの方法、特にユーザー認証や情報連携でよく使われる技術なんです。

JWTは、ピリオド(.)で区切られた3つの部分から成り立っています。

ヘッダー.ペイロード.署名

それぞれの部分はこんな感じです。

  • ヘッダー
    トークンの種類(JWTだよ!)や、どの署名アルゴリズムを使っているか(例えばRS256とか)の情報が入ります。
  • ペイロード
    ユーザーIDや名前、権限といった、伝えたい実際の情報(クレームと呼びます)が入ります。ただし、ここは暗号化されているわけではないので、パスワードのような秘密の情報は入れちゃダメです。
  • 署名
    ヘッダーとペイロードを、設定された秘密鍵で署名したもの。情報が改ざんされていないかを確認するために使います。

イメージとしては、こんな感じの箱に入っているようなものです。

+-----------------+      +-----------------+      +-----------------+
|   ヘッダー       | ---- |    ペイロード    | ---- |      署名        |
| (alg, typ)      |      | (sub, name, exp)|      | (秘密鍵で計算)  |
+-----------------+      +-----------------+      +-----------------+
       (Base64URLエンコード)   (Base64URLエンコード)

便利そうですよね?でも、使い方を間違えると大変なことになります。例えば、署名の検証をサボったり、簡単な秘密鍵を使ったりすると、悪意のある第三者に情報を盗まれたり、なりすまされたりする危険があるのです。

だからこそ、JWTのベストプラクティスを知って、それを守ることがセキュアコーディングの観点からめちゃくちゃ大事になってきます。安全な使い方をしっかり学んでいきましょう。

JWTベストプラクティスの核心!アルゴリズム選定と鍵管理

JWTを安全に使う上で、絶対に外せないのが「署名アルゴリズムの選び方」と「鍵の管理方法」です。この二つはセキュリティの根幹に関わる部分なので、しっかり押さえておきましょう。

署名アルゴリズムは、JWTが改ざんされていないことを保証するための計算方法です。いくつか種類がありますが、適切なものを選ばないとセキュリティの意味がなくなってしまいます。

そして、その計算に使う「鍵」が漏れてしまったら元も子もありません。鍵の管理は、金庫の鍵を管理するのと同じくらい慎重に行う必要があるんですよ。

署名アルゴリズムはどれを選ぶべきか

JWTの署名アルゴリズムには、大きく分けて二つのタイプがあります。

  • 対称鍵暗号 (HMAC系: HS256, HS384, HS512)
    同じ一つの秘密鍵を使って署名の生成と検証を行います。シンプルですが、署名を検証する側も秘密鍵を知っている必要があります。
  • 非対称鍵暗号 (RSA/ECDSA系: RS256, ES256など)
    秘密鍵で署名を生成し、対になる公開鍵で署名を検証します。検証側は公開鍵だけあれば良いので、秘密鍵を安全に保ちやすいメリットがあります。

どちらが良いかは状況にもよりますが、一般的には非対称鍵暗号(RS256やES256など)を使うことが強く推奨されます。理由は、署名検証に必要な公開鍵は漏れても(比較的)安全なのに対して、署名生成に使う秘密鍵は絶対に漏れてはいけないからです。

非対称鍵方式なら、秘密鍵を守る範囲を限定できるため、より安全な設計がしやすいのです。HS256のような対称鍵を使う場合は、鍵の共有と管理に細心の注意が必要になります。

脆弱性の元凶!alg noneを絶対に許可しない設定

JWTの仕様には、署名アルゴリズムとして`none`を指定できる、というちょっと怖い部分があります。`alg: none`は、署名をしない、つまり検証が不要であるという意味になります。

攻撃者は、ヘッダーを書き換えて`alg`を`none`にし、署名を空にして送りつけることで、システムに不正なトークンを受け入れさせようとすることがあります。

絶対に`alg: none`を受け入れてはいけません! ほとんどのJWTライブラリでは、デフォルトで`none`を許可しない設定になっていたり、明示的に許可するアルゴリズムを指定する機能があったりします。

利用しているライブラリのドキュメントを確認し、`none`アルゴリズムを確実に拒否するように設定してください。

例えば、ライブラリによってはこんな感じで許可するアルゴリズムを指定できます(これはあくまでイメージです)。

// 許可するアルゴリズムのリストを指定する例 (疑似コード)
options = {
  algorithms: ['RS256', 'ES256'] // HS256やnoneは許可しない
};
jwt.verify(token, publicKey, options);

必ず、利用するライブラリでの正しい設定方法を確認しましょう。

最重要!秘密鍵と公開鍵の安全な管理方法

署名アルゴリズムを選んだら、次は鍵の管理です。特に非対称鍵暗号(RS256など)で使う「秘密鍵」や、対称鍵暗号(HS256など)で使う「秘密鍵」は、絶対に外部に漏れてはいけません。もし漏洩したら、誰でも有効なJWTを偽造できてしまいます。

やってはいけない代表例は、ソースコードの中に鍵を直接書き込む(ハードコーディングする)ことです。これは絶対に避けましょう。Gitリポジトリなどに鍵が含まれてしまうと、大変なことになります。

安全な鍵の管理方法には、以下のようなものがあります。

  • 環境変数を使う
    サーバーの環境変数として鍵を設定し、プログラムから読み込む方法。コードと鍵を分離できます。
  • 設定ファイルを使う
    専用の設定ファイルに鍵を記述し、そのファイルへのアクセス権限を厳格に管理する方法。環境変数より管理しやすい場合も。
  • シークレット管理サービスを使う
    AWS Secrets Managerや Google Secret Manager、HashiCorp Vaultのような専用サービスを利用する方法。より安全で高度な管理が可能です。特に本番環境では、これらのツールの利用を強く推奨します

また、鍵は定期的に変更(ローテーション)することもセキュリティを高める上で有効な手段です。面倒に感じるかもしれませんが、安全のためには必要な作業だと考えましょう。

実装で差がつく!JWTベストプラクティス トークン管理と検証

アルゴリズムと鍵管理の基本を押さえたら、次はJWTを実際にシステムで使う際の具体的な注意点を見ていきましょう。

トークンの寿命をどうするか、どうやって検証するかなど、実装レベルでのベストプラクティスを守ることで、さらにセキュリティを高めることができます。セキュアコーディングは細部への配慮が肝心ですよ!

トークンの有効期限は短く設定する

JWTのペイロードには、`exp` (Expiration Time) というクレームを含めることができます。これはトークンの有効期限を示すタイムスタンプです。アクセストークン(APIアクセスなどに使うJWT)の有効期限は、可能な限り短く設定するのがベストプラクティスです。

なぜなら、もしトークンが何らかの理由で漏洩した場合でも、有効期限が短ければ悪用される期間を限定できるからです。目安としては数分から長くても1時間程度が良いでしょう。

短すぎるとユーザーが頻繁に再ログインを求められる可能性があるので、アプリケーションの特性に合わせてバランスを取ることが求められます。有効期限切れのチェックは、JWTを受け取る側で必ず行いましょう。

リフレッシュトークンを賢く利用する

アクセストークンの有効期限を短くすると、ユーザー体験が悪くなる可能性があります。そこで登場するのが「リフレッシュトークン」です。

リフレッシュトークンは、アクセストークンとは別に発行される、有効期間が比較的長い(数日~数週間など)トークンです。アクセストークンの有効期限が切れた際に、ユーザーに再ログインを強いる代わりに、このリフレッシュトークンを使って新しいアクセストークンを再発行することができます。

フローのイメージはこんな感じです。

ユーザー ---> 認証サーバー : ログイン情報
認証サーバー ---> ユーザー : アクセストークン(短命), リフレッシュトークン(長命)

ユーザー ---> APIサーバー : アクセストークン (期限切れ!)
APIサーバー ---> ユーザー : エラー (期限切れだよ)

ユーザー ---> 認証サーバー : リフレッシュトークン
認証サーバー ---> ユーザー : 新しいアクセストークン(短命)

ユーザー ---> APIサーバー : 新しいアクセストークン
APIサーバー ---> ユーザー : OK!リソースを返すよ

リフレッシュトークン自体も漏洩すると危険なので、安全な方法で保管し(通常はサーバーサイドのDBなどで管理します)、適切な有効期限を設定し、不要になったら失効させる仕組みが必要です。アクセストークンより厳重な管理が求められます。

トークン検証は厳格に行う 署名有効期限発行者利用者

JWTを受け取ったサーバーは、そのトークンが本当に正当なものか、厳格に検証する必要があります。検証すべき項目は主に以下の通りです。

  • 署名の検証
    ヘッダーで指定されたアルゴリズムと、サーバーが保持している公開鍵(または秘密鍵)を使って、署名が正しいか検証します。改ざんされていないかを確認する最も基本的なチェックです。
  • 有効期限 (exp) の検証
    ペイロード内の`exp`クレームを見て、トークンの有効期限が切れていないか確認します。期限切れのトークンは拒否しなければなりません。
  • 発行者 (iss) の検証
    ペイロード内の`iss` (Issuer) クレームを見て、トークンが信頼できる発行元から発行されたものか確認します。予期しない発行元のトークンは拒否します。
  • 利用者 (aud) の検証
    ペイロード内の`aud` (Audience) クレームを見て、トークンが意図された利用者(通常は自分自身のサービス)向けのものか確認します。他のサービス向けのトークンを誤って受け入れないようにします。

これらの検証は、JWTライブラリが提供する検証機能を適切に使うことで、比較的簡単に実装できます

ただし、ライブラリのデフォルト設定だけに頼らず、必要な検証項目がすべて有効になっているか、必ず確認してください。どれか一つでも怠ると、脆弱性につながる可能性があります。

通信は必ずHTTPSで行う

これはJWTに限った話ではありませんが、非常に重要です。JWTは通常、HTTPリクエストのヘッダー(Authorizationヘッダーなど)に入れて送信されます。もし通信が暗号化されていないHTTPで行われていると、ネットワーク経路上でトークンが盗聴される危険があります。

トークンが盗まれれば、攻撃者はそのトークンを使って正規のユーザーになりすますことができてしまいます。

これを防ぐため、JWTをやり取りする通信は、必ずHTTPS(SSL/TLSによる暗号化通信)で行う必要があります。Webサーバーの設定で、常にHTTPS接続を強制するようにしましょう。また、HSTS (HTTP Strict Transport Security) ヘッダーを設定することも、より安全性を高める上で有効です。

避けるべきJWTのアンチパターン - ある間違いと対策

ここでは、JWTを使う際についついやってしまいがちな間違い、つまり「アンチパターン」とその対策について解説します。これらを知っておくことで、意図せず脆弱なシステムを作ってしまうリスクを減らせます。

セキュアコーディングでは、正しい方法を知るだけでなく、間違った方法を避けることも同じくらい大事なのです。

ペイロードに機密情報や個人情報を入れない

繰り返しになりますが、JWTのペイロードは暗号化されているわけではなく、Base64URLエンコードされているだけです。これは、ちょっと知識があれば誰でも簡単に元の情報(JSON形式)に戻せてしまいます。

そのため、パスワード、APIキー、クレジットカード番号、住所、電話番号といった機密性の高い情報や、詳細な個人情報をペイロードに含めてはいけません。ペイロードに含めて良いのは、ユーザーIDやロール(役割)名など、万が一漏れても直接的な被害につながりにくい情報に限定すべきです。

もし機密情報を扱いたい場合は、JWTとは別の、適切に暗号化された方法でやり取りする必要があります。

JWTをセッション管理の代替と誤解しない

JWTは、サーバー側で状態を持たない「ステートレス」な認証方式として使われることが多いです。つまり、トークン自体に必要な情報が含まれており、サーバーは受け取ったトークンを検証するだけでユーザーを認証できます。これはスケーラビリティの面でメリットがあります。

しかし、JWTを従来のセッション管理(サーバー側でログイン状態などを保持する方式)と全く同じものと考えてはいけません。

特に問題になるのが「トークンの失効」です。一度発行したJWTは、有効期限が切れるまで基本的に有効です。もしユーザーがログアウトしたり、パスワードを変更したりした場合でも、そのユーザーの古いJWTが第三者の手に渡っていれば、有効期限内は使われ続ける可能性があります。

JWTだけで完璧なセッション管理(特に能動的なログアウトや即時失効)を実現するのは難しいのです。

これを解決するには、失効したトークンのリスト(ブラックリスト)をサーバー側で管理したり、リフレッシュトークンと組み合わせてアクセストークンの有効期間を極端に短くしたりするなどの追加対策が必要になります。

JWTの特性を理解し、必要に応じてサーバーサイドでの状態管理も組み合わせることを検討しましょう。

【まとめ】JWTベストプラクティスで安全な認証基盤を

お疲れ様でした! JWTのベストプラクティスについて、基本的な考え方から具体的な実装の注意点、避けるべきアンチパターンまで解説してきました。JWTは非常に便利な技術ですが、安全に使うためには正しい知識と慎重な実装が欠かせません。

最後に、この記事で紹介したJWTベストプラクティスの要点をまとめておきましょう。

  • アルゴリズム選定 `RS256`などの非対称鍵暗号を推奨し、`none`は絶対に許可しない。
  • 鍵管理 秘密鍵は環境変数やシークレット管理サービスで安全に管理し、ハードコーディングしない。
  • 有効期限 アクセストークンの有効期限は短く設定し、リフレッシュトークンを活用する。
  • 厳格な検証 署名、有効期限(`exp`)、発行者(`iss`)、利用者(`aud`)を必ず検証する。
  • HTTPS通信 JWTのやり取りは常にHTTPSで行う。
  • ペイロード注意 機密情報や個人情報をペイロードに含めない。
  • セッション誤用回避 JWTのステートレス性を理解し、必要ならサーバー側での状態管理も検討する。

これらのベストプラクティスを意識してJWTを扱うことで、より安全な認証・認可の仕組みを構築できます。ぜひ、皆さんの開発プロジェクトでも、この記事の内容を参考にJWT ベストプラクティスに基づいたセキュアコーディングを実践してみてくださいね。

次の一歩として、現在使っているJWTライブラリのドキュメントを再確認したり、既存のコードに改善点がないか見直してみるのも良いでしょう。安全なウェブアプリケーション開発のために、学び続けていきましょう!

このブログを検索

  • ()

自己紹介

自分の写真
リモートワークでエンジニア兼Webディレクターとして活動しています。プログラミングやAIなど、日々の業務や学びの中で得た知識や気づきをわかりやすく発信し、これからITスキルを身につけたい人にも役立つ情報をお届けします。 note → https://note.com/yurufuri X → https://x.com/mnao111

QooQ