
モダンアプリケーションにおける認証トークンの安全な保管方法
フロントエンド開発者向けのガイドが、Web アプリケーションにおける認証トークン保管の複数の手法とセキュリティ上のトレードオフを比較分析している。localStorage、メモリ内変数、httpOnly Cookie のほか、JWT、セッションベース認証、OAuth パターン、React での実装方法を検討している。
開発者10人に認証トークンの保管方法を尋ねると4つの異なる答えが返ってくるほど、業界での合意が不在である。localStorage は任意の JavaScript(npm パッケージやブラウザ拡張機能を含む)からアクセス可能で、XSS(クロスサイトスクリプティング)攻撃によって 1 行のコードでトークンが盗難される危険がある。一方、httpOnly Cookie フラグは JavaScript による document.cookie 経由のアクセスを防止し、Secure フラグは HTTPS 通信のみに限定する。
トークン保管方法の比較
localStorage は便利だが XSS 脆弱性に対して無防備である。メモリ内変数は localStorage よりも盗難が困難だが、ページをリロードするとトークンが失われる。httpOnly Cookie にトークンを保存する場合、CSRF(クロスサイトリクエストフォージェリ)攻撃の対策が必要になる。
SameSite=Lax フラグは異なるサイトからのリクエストに Cookie を添付しないようにするが、トップレベルの GET ナビゲーション時には例外がある。GET リクエストで金銭移動が可能な場合、Lax は容易に偽造され、またはフレームワークの「method override」を使って GET を POST に偽装する攻撃者によっても悪用される。最も安全な設定は CSRF トークン、SameSite、オリジン検証の3つの対策を組み合わせることである。__Host- プレフィックスは Cookie に対して Secure フラグと Path=/ を必須とする。
JWT とセッションベース認証
JWT は header、payload、signature の3つの部分をドット区切りで構成する文字列であり、サーバーが生成した正確なペイロードであることを暗号学的に証明する。JWT は状態を持たないとされるのは、サーバーが署名を再計算し、データベースを参照することなく検証できるためである。ただし JWT は有効期限まで失効させられず、取り消す手段がない。チュートリアルでよく見かける JWT の有効期限は 7 日間である。
セッションベース認証は長い乱数文字列を Cookie に保存し、サーバー側のユーザー行と照合する方法である。ログイン時に新規セッション ID を生成し、二度と再利用しないことでセッション固定化攻撃を防止する。セッションのログアウトはサーバー側の行を即座に削除し、すべてのデバイスで無効化される。セッションベース認証はステートレス JWT と異なり、取り消しと即座のログアウトが可能である。
OAuth パターンと トークンローテーション
OAuth アクセストークンは短期間(5~15 分間)有効でメモリに保存される。リフレッシュトークンは httpOnly Cookie に保存され、パスを /api/refresh にスコープすべきである。トークンローテーションは新しいトークン発行時に古いトークンを無効化する単一使用化手法である。再利用検出により、トークンが 2 回提示された場合は盗難を検出できる。単一使用トークンが 2 度現れることは通常の動作では不可能であり、盗難の兆候である。
2025 年初期に公開された RFC 9700 は、ブラウザ OAuth クライアントに対してトークンローテーション、またはそれに相当する機能を要求している。複数の同時リクエストがすべてトークン有効期限に達した場合、5 つのリクエストすべてが /api/refresh を同時に呼び出す可能性がある。
crypto.randomBytes により生成される乱数セッション ID の長さは 32 バイトである。
この記事は元記事の事実のみに基づいて自動生成されました。
筆者の見立て
- トークン保管方法に関する議論が解決に至らないのは、各アプローチが異なる懸念事項に対応しているためだと解釈している。
- localStorage から盗まれたキーはタブを閉じた後も機能し続けるのに対し、メモリ内変数は有効なセッションに限定される点で、前者が明らかに悪いと論じている。
- 現実的な目標は XSS バグの発生頻度を減らし、発生時の被害を縮小することだと解釈している。
- プレーン変数は localStorage よりも意味のあるレベルで盗難が困難だと解釈している。
- JWT のステートレス性は存在しない拡張性の問題を解決し、不要な取り消し問題を作成したと解釈している。
- 単一のバックエンドが単一のフロントエンドに対応する場合、退屈な 2005 年のデザイン(セッション)が勝利すると見解を示している。
- JWT が輝く場面はトークン検証がデータベース共有より優れている場合だと解釈している。
- OAuth アクセストークンの盗難について、攻撃者の最悪シナリオは無効化されるまでの 15 分間の窓口であると解釈している。
- 盗難された認証情報はローテーションと再利用検出を通じて自動的に無効化されると解釈している。
- メモリ内 OAuth トークン + Cookie リフレッシュトークンの分割パターンは本当に優れており、トークンがブラウザに触れる範囲では最善の方法だと見解を示している。
- トークンローテーション有効化後に不可解なログアウト報告が増えた場合、同時リフレッシュの猶予期間の問題がほぼ常に原因だと予想している。
出典
Neciu Dan「What's the best way to do authentication in modern applications」https://neciudan.dev/most-secure-way-to-store-auth-token (Auth0、Okta の報道による)