セッション管理
Session Management Cheat Sheet
- 原典
- Session Management Cheat Sheet(OWASP Cheat Sheet Series)
- 原文
- GitHub 上の Markdown
- 底本
bac04fb5(2026-07-29 時点)- ライセンス
- CC BY-SA 4.0(原典と同一。訳文も同ライセンスで再配布できます)
はじめに
Web の認証、セッション管理、アクセス制御
Web セッションは、同じ利用者に紐づく HTTP のリクエストと応答のやりとりの連なりである。 現代の複雑な Web アプリケーションは、複数のリクエストにわたって利用者ごとの情報や状態を保持することを必要とする。 したがってセッションは、その期間中に利用者が Web アプリケーションと行うすべてのやりとりに適用される変数(アクセス権や地域化の設定など)を確立する能力を与える。
Web アプリケーションは、最初のリクエストのあと匿名の利用者を追跡するためにセッションを作れる。 利用者の言語の好みを保持するのがその例である。 加えて Web アプリケーションは、利用者が認証されたあともセッションを使う。 これによりその後のリクエストで利用者を識別でき、安全性のためのアクセス制御を適用し、利用者の非公開データへの認可されたアクセスを与え、アプリケーションの使いやすさを高められる。 したがって現在の Web アプリケーションは、認証の前後の両方でセッションの機能を提供しうる。
認証済みのセッションが確立されると、セッション ID(トークン)は一時的に、そのアプリケーションが使う最も強固な認証手段と等価になる。 ユーザー名とパスワード、パスフレーズ、ワンタイムパスワード(OTP)、クライアント側のデジタル証明書、スマートカード、生体情報(指紋や網膜など)と同等である。 認証も参照する。
HTTP は状態を持たないプロトコルであり(RFC2616 の 5 節)、リクエストと応答の各組は他のやりとりから独立している。 したがってセッションという概念を導入するには、Web アプリケーションで一般に使える認証とアクセス制御(認可)のモジュールの両方を結びつけるセッション管理の機能を実装する必要がある。

セッション ID(トークン)は、利用者の認証の資格情報を(利用者セッションという形で)その利用者の HTTP 通信と、Web アプリケーションが強制する適切なアクセス制御に結びつける。 現代の Web アプリケーションにおけるこの三つの構成要素(認証、セッション管理、アクセス制御)の複雑さに加え、その実装と結びつけが Web 開発者の手に委ねられている(開発のフレームワークがこれらのモジュールのあいだの厳格な関係を提供しないため)という事実が、安全なセッション管理のモジュールの実装を非常に難しくしている。
セッション ID の露出、捕捉、予測、総当たり、固定は、セッションの乗っ取り(サイドジャッキング)攻撃につながる。 そこで攻撃者は、Web アプリケーションにおいて被害者の利用者に完全になりすませる。 攻撃者が行えるセッション乗っ取りには、標的型と一般型の二種類がある。 標的型では、攻撃者の目的は特定の(あるいは権限を持つ)被害者の利用者になりすますことである。 一般型では、攻撃者の目的はその Web アプリケーションの任意の正当な利用者になりすます(あるいはその立場でアクセスを得る)ことである。
セッション ID の性質
認証された状態を保ち、Web アプリケーション内での利用者の進行を追跡するため、アプリケーションはセッションの生成時に割り当てられるセッション識別子(セッション ID、トークン)を利用者へ与える。
それはセッションの期間中、利用者と Web アプリケーションのあいだで共有され交換される(すべての HTTP リクエストで送られる)。
セッション ID は name=value の組である。
安全なセッション ID を実装するには、識別子の生成が次の性質を満たさなければならない。
セッション ID の名前からの推定
セッション ID に使う名前は、極端に説明的であるべきではなく、その ID の目的や意味についての不必要な詳細を与えるべきではない。
最も一般的な Web アプリケーション開発のフレームワークが使うセッション ID の名前は、容易に推定できる。
PHPSESSID(PHP)、JSESSIONID(J2EE)、CFID と CFTOKEN(ColdFusion)、ASP.NET_SessionId(ASP .NET)などである。
したがってセッション ID の名前は、その Web アプリケーションが使う技術とプログラミング言語を露出させうる。
開発フレームワークの既定のセッション ID の名前を、id のような一般的な名前に変えることが推奨される。
セッション ID のエントロピー
セッション識別子は、総当たりによるセッションの推測攻撃を防ぐため、少なくとも 64 ビットのエントロピーを持たなければならない。
エントロピーは、ある値におけるランダム性や予測不能性の量を指す。
エントロピーの各「ビット」はありうる結果の数を倍にするので、64 ビットのエントロピーを持つセッション ID は 2^64 通りの値をとりうる。
セッション ID の生成には強固な CSPRNG(暗号論的に安全な疑似乱数生成器)を使わなければならない。 これにより生成される値がありうるすべての値のあいだに均等に分布する。 そうでなければ、攻撃者が統計的な解析の手法を使ってセッション ID の生成のされ方にパターンを見出せる可能性があり、実効的なエントロピーが下がって、正当なセッション ID をより容易に推測または予測できるようになる。
注記
- 攻撃者が正当なセッション ID を総当たりするのに要する期待時間は、エントロピーのビット数、有効なセッションの数、セッションの有効期限、攻撃者の推測の速度といった要素によって決まる。
- Web アプリケーションが 64 ビットのエントロピーを持つセッション ID を生成する場合、攻撃者が毎秒 1 万回の推測を試せて、アプリケーションに同時に有効なセッションが 10 万件あると仮定すると、正当なセッション ID の推測に成功するまでにおよそ 585 年を費やすことになる。
- 攻撃者がセッション識別子を総当たりするのに要する期待時間のさらなる分析はこちらにある。
セッション ID の長さ
前のセッション ID のエントロピーの節で述べたように、セッション ID の主要な安全上の要件は、総当たりによる推測攻撃を防ぐために少なくとも 64 ビットのエントロピーを含むことである。
セッション ID の長さも重要であるが、安全性を担保するのはエントロピーである。
セッション ID は十分なエントロピーを符号化できる長さでなければならず、それによって攻撃者が正当なセッション ID を推測する総当たり攻撃を防ぐ。
同じ量のエントロピーでも、エンコードの方法によって長さは異なる。 セッション ID はしばしば 16 進のエンコードで表現される。 16 進のエンコードを使う場合、必要な 64 ビットのエントロピーを得るには、セッション ID は少なくとも 16 文字の長さでなければならない。 異なるエンコード(Base64 や ASP.NET のセッション ID に対する Microsoft のエンコードなど)を使う場合、最低 64 ビットのエントロピーを表すのに必要な文字数は異なる。
セッション ID の一部が固定または予測可能であれば、実効的なエントロピーは下がり、それを補うために長さを増やす必要が生じうることに注意する。 たとえば 16 文字の 16 進のセッション ID の半分が固定されていれば、ランダムなのは残りの 8 文字だけであり、エントロピーは 32 ビットしかない。 これは強固な安全性には不十分である。 安全性を保つには、セッション ID の全体がランダムに生成され予測できない状態にするか、一部がランダムでないなら全体の長さを増やす。
注記
- セッション ID の長さとエントロピーの関係についてさらに詳しくはこちらにある。
セッション ID の内容(値)
セッション ID の内容(値)は、情報露出の攻撃を防ぐために意味を持たないものでなければならない。 そうでなければ攻撃者が ID の内容を復号し、利用者、セッション、あるいは Web アプリケーションの内部の仕組みの詳細を引き出せる。
セッション ID はクライアント側では単なる識別子でなければならず、その値には機微な情報や個人を特定できる情報(PII)を決して含めてはならない。 PII についてさらに詳しくは Wikipedia やこの記事を参照する。
セッション ID に紐づく意味と、業務やアプリケーションのロジックは、サーバー側、具体的にはセッションのオブジェクトか、セッション管理のデータベースやリポジトリに保存しなければならない。
保存する情報には、クライアントの IP アドレス、User-Agent、メールアドレス、ユーザー名、利用者 ID、役割、権限の水準、アクセス権、言語の好み、アカウント ID、現在の状態、最終ログイン、セッションのタイムアウト、その他の内部のセッションの詳細を含められる。 セッションのオブジェクトや属性がクレジットカード番号のような機微な情報を含む場合は、セッション管理のリポジトリを適切に暗号化して保護する必要がある。
使用する言語やフレームワークが生成するセッション ID を使うことが推奨される。 自前でセッション ID を作る必要がある場合は、少なくとも 128 ビットの大きさで暗号論的に安全な疑似乱数生成器(CSPRNG)を使い、各セッション ID が一意である状態にする。
セッション管理の実装
セッション管理の実装は、セッション ID を共有し継続的に交換するために利用者と Web アプリケーションのあいだで使う交換の仕組みを定める。 Web アプリケーションでセッションの状態を保つ仕組みは HTTP に複数ある。 Cookie(標準の HTTP ヘッダ)、URL のパラメータ(URL の書き換え。RFC2396)、GET リクエストの URL の引数、POST リクエストの本体の引数(HTML フォームの隠し項目など)、独自の HTTP ヘッダである。
望ましいセッション ID の交換の仕組みは、トークンの有効期限や細かい利用の制約といった高度なトークンの性質を定義できるものであるべきである。 これは Cookie(RFC 2109、2965、6265)が最も広く使われるセッション ID の交換の仕組みの一つである理由でもあり、他の方法では使えない高度な機能を提供する。
ID を URL に含める仕組みなど、特定のセッション ID の交換の仕組みを使うと、セッション ID が露出しうる(Web のリンクやログ、ブラウザの履歴とブックマーク、Referer ヘッダ、検索エンジンにおいて)。 また ID の操作やセッション固定攻撃のような他の攻撃を容易にする。
組み込みのセッション管理の実装
J2EE、ASP .NET、PHP などの開発フレームワークは、独自のセッション管理の機能とその実装を提供する。 ゼロから自前のものを作るのではなく、これらの組み込みの仕組みを使うことが推奨される。 それらは世界中の多様な Web の環境で使われており、時間をかけて Web アプリケーションの安全性と開発のコミュニティによって検証されてきた。
ただしこれらのフレームワークも過去に脆弱性と弱点を示してきたので、既知の脆弱性がすべて修正されている可能性のある最新版を常に使い、既定の設定を見直して、この文書で述べる推奨に従って安全性を高めるよう変更することが推奨される。
セッション管理の仕組みがセッション ID を一時的に保存するのに使う保存の機能やリポジトリは安全でなければならず、ローカルやリモートでの偶発的な露出や不正なアクセスからセッション ID を保護しなければならない。
使う交換の仕組みと受け付ける交換の仕組み
Web アプリケーションはセッション ID の交換の管理に Cookie を使うべきである。 利用者が URL のパラメータのような別の交換の仕組みでセッション ID を送ってきた場合、セッション固定を止める防御の方針として、それを受け付けることを避けるべきである。
注記
- Web アプリケーションが既定のセッション ID の交換の仕組みとして Cookie を使っていても、他の交換の仕組みも受け付けていることがある。
- したがって、セッション ID を処理し管理する際にその Web アプリケーションが現在受け付けているすべての仕組みを徹底的な検査によって確認し、受け付けるセッション ID の追跡の仕組みを Cookie だけに限定する必要がある。
- 過去には、URL のパラメータを使う、あるいは特定の条件が満たされたときに(自動の URL 書き換えによって)Cookie から URL のパラメータへ切り替える Web アプリケーションがあった(Cookie に対応しない Web クライアントの識別や、利用者のプライバシーの懸念により Cookie を受け付けない場合の検出など)。
トランスポート層セキュリティ
セッション ID の交換を、ネットワークの通信における能動的な盗聴と受動的な露出から保護するには、利用者の資格情報が交換される認証の処理だけでなく、Web セッションの全体にわたって暗号化された HTTPS(TLS)の接続を使うことが不可欠である。 これに対応するクライアントであれば、HTTP Strict Transport Security(HSTS)によって緩和できる。
加えて、セッション ID が暗号化された経路でのみ交換される状態にするため、Secure の Cookie 属性を使わなければならない。
暗号化された通信路を使うことは、攻撃者が通信を傍受して操作し、被害者のブラウザにセッション ID を注入(固定)する一部のセッション固定攻撃からもセッションを守る。
以下の最良の慣行は、(とくに Cookie を使う場合の)セッション ID の保護と、Web アプリケーションへの HTTPS の統合の助けに焦点を置く。
- あるセッションを HTTP から HTTPS へ、あるいはその逆へ切り替えない。これはネットワーク上でセッション ID を平文で露出させる。
- HTTPS へリダイレクトする場合は、リダイレクトが起きたあとに Cookie を設定または再生成する状態にする。
- 同じページ、あるいは同じドメインにおいて、暗号化された内容と暗号化されていない内容(HTML のページ、画像、CSS、JavaScript のファイルなど)を混在させない。
- 可能であれば、公開の暗号化されていない内容と、非公開の暗号化された内容を同じホストから提供することを避ける。安全でない内容が必要な場合は、別の安全でないドメインで提供することを検討する。
- HTTPS の接続を強制するために HTTP Strict Transport Security(HSTS)を実装する。
TLS を安全に実装する一般的な指針はトランスポート層セキュリティを参照する。
TLS はセッション ID の予測、総当たり、クライアント側の改変、固定に対しては保護を与えないことを強調しておく。 ただし中間者攻撃によって攻撃者がセッション ID を傍受したり盗んだりすることに対しては、有効な保護を与える。
Cookie
Cookie に基づくセッション ID の交換の仕組みは、セッション ID の交換を保護するために使える Cookie の属性という形で、複数の安全機能を提供する。
Secure 属性
Secure の Cookie 属性は、暗号化された HTTPS(SSL/TLS)の接続でのみ Cookie を送るようブラウザに指示する。
このセッション保護の仕組みは、中間者攻撃によるセッション ID の露出を防ぐために必須である。
攻撃者がブラウザの通信からセッション ID を単純に捕捉することはできなくなる。
(Web アプリケーションのホストで TCP/80(HTTP)が閉じられていても)通信に HTTPS のみを使うよう Web アプリケーションに強制することは、Secure の Cookie が設定されていなければセッション ID の露出を防がない。
ブラウザは、暗号化されていない HTTP の接続でセッション ID を露出させるよう騙されうる。
攻撃者は被害者の通信を傍受して操作し、暗号化されていない HTTP での Web アプリケーションへの参照を注入して、ブラウザにセッション ID を平文で送らせられる。
SecureFlag も参照する。
HttpOnly 属性
HttpOnly の Cookie 属性は、スクリプト(JavaScript や VBScript など)が DOM の document.cookie オブジェクト経由で Cookie にアクセスすることを許さないようブラウザに指示する。
このセッション ID の保護は、XSS 攻撃によるセッション ID の窃取を防ぐために必須である。
ただし XSS 攻撃が CSRF 攻撃と組み合わされた場合、ブラウザはリクエストの送信時に常に Cookie を含めるので、Web アプリケーションへ送られるリクエストにはセッションの Cookie が含まれる。
HttpOnly の Cookie は Cookie の機密性のみを保護する。
攻撃者は XSS 攻撃の文脈の外で、それをオフラインで使うことはできない。
クロスサイトスクリプティングの防止を参照する。
HttpOnly も参照する。
SameSite 属性
SameSite 属性は、サイトをまたぐリクエストでブラウザが Cookie を送ることを防ぎ、生成元をまたぐ漏出を緩和して CSRF への防御を与える。
セッションの Cookie には SameSite=Strict(望ましい)または SameSite=Lax を明示的に設定しなければならない。
Secure なしに SameSite=None を決して使わない。
またブラウザとバージョンによって異なる既定値に依存しない。
SameSite も参照する。
Cookie 名の接頭辞
Cookie 名の接頭辞を使い、ブラウザの水準で Cookie を安全性の性質に結びつける(RFC 6265bis 4.1.3 節)。
__Host-。その Cookie はSecure付きで設定されなければならず、Domain属性を持ってはならず、Path=/を使わなければならない。サブドメインからの偽造と HTTPS の格下げ攻撃を防ぐ。セッション ID に推奨される。__Secure-。その Cookie はSecure付きで設定されなければならない。サブドメインでの共有が必要な場合にのみ使う。
例を示す。
Set-Cookie: __Host-SessionID=<value>; Secure; HttpOnly; SameSite=Strict; Path=/
Domain 属性と Path 属性
Domain の Cookie 属性は、指定したドメインとそのすべてのサブドメインにのみ Cookie を送るようブラウザに指示する。
この属性が設定されていなければ、既定では生成元のサーバーにのみ Cookie が送られる。
Path の Cookie 属性は、Web アプリケーション内の指定したディレクトリやその下位(パスやリソース)にのみ Cookie を送るようブラウザに指示する。
この属性が設定されていなければ、既定では要求されて Cookie を設定したリソースのディレクトリ(パス)に対してのみ Cookie が送られる。
この二つの属性には、狭い、あるいは制限された範囲を使うことが推奨される。
したがって Domain 属性は設定せず(Cookie を生成元のサーバーに限定し)、Path 属性はセッション ID を使う Web アプリケーションのパスに対して可能なかぎり制限的に設定するべきである。
Domain 属性を example.com のように緩い値に設定すると、同じドメインに属する異なるホストや Web アプリケーションのあいだでセッション ID に対する攻撃を仕掛けられる。
これはサブドメインをまたぐ Cookie として知られる。
たとえば www.example.com の脆弱性によって、攻撃者が secure.example.com のセッション ID へアクセスできるようになりうる。
加えて、安全性の水準が異なる Web アプリケーションを同じドメインに混在させないことが推奨される。
一方の Web アプリケーションの脆弱性によって、攻撃者が緩い Domain 属性(example.com など)を使い、同じドメインの別の Web アプリケーションのセッション ID を設定できるようになる。
これはセッション固定攻撃に使える手法である。
Path 属性は同じホスト上の異なるパスを使う Web アプリケーションのあいだでセッション ID を分離できるが、(とくに安全性の水準や範囲が異なる)複数の Web アプリケーションを同じホストで動かさないことが強く推奨される。
これらのアプリケーションは document.cookie オブジェクトのような他の方法でセッション ID へアクセスできる。
またどの Web アプリケーションも、そのホスト上の任意のパスに対して Cookie を設定できる。
Cookie は DNS の偽装、乗っ取り、汚染の攻撃に脆弱であり、攻撃者は DNS の解決を操作して、あるホストやドメインのセッション ID をブラウザに露出させられる。
Expire 属性と Max-Age 属性
Cookie に基づくセッション管理の仕組みは、二種類の Cookie を使える。
持続しない(セッション)Cookie と、持続する Cookie である。
Cookie が Max-Age(Expires より優先される)または Expires の属性を持つ場合、それは持続する Cookie とみなされ、有効期限までブラウザによってディスクに保存される。
通常、認証後に利用者を追跡するセッション管理の機能は、持続しない Cookie を使う。 これにより、現在のブラウザのインスタンスが閉じられればセッションはクライアントから消える。 したがってセッション管理の目的には持続しない Cookie を使うことが強く推奨される。 そうすればセッション ID が長期間クライアントのキャッシュに残り、攻撃者がそこから入手することを避けられる。
- 機微な情報を持続させず、暗号化し、必要な期間だけ保存することによって、それが侵害されない状態にする
- Cookie の操作によって認可されていない活動が行えない状態にする
- 通信路上での偶発的な安全でない送信を防ぐため、secure のフラグが設定されている状態にする
- アプリケーションのコードにおけるすべての状態遷移が、Cookie を適切に確認しその使用を強制しているかを判断する
- 機微なデータを Cookie に持続させる場合は、Cookie の全体を暗号化する状態にする
- アプリケーションが使うすべての Cookie、その名前、そしてなぜそれが必要なのかを定義する
HTML5 の Web Storage API
Web Hypertext Application Technology Working Group(WHATWG)は、HTML5 の Web Storage API である localStorage と sessionStorage を、クライアント側で名前と値の組を保存する仕組みとして記述している。
HTTP の Cookie と違い、localStorage と sessionStorage の内容はブラウザによってリクエストや応答の中で自動的に共有されることはなく、クライアント側でデータを保存するために使われる。
localStorage API
警告 認証のトークン、セッション ID、JWT、リフレッシュトークン、その他いかなる資格情報も
localStorageやsessionStorageに保存しない。これらの API はそのオリジンで実行されるあらゆる JavaScript からアクセスできるので、XSS 脆弱性が一つあればすべてのトークンが露出する。HttpOnly; Secure; SameSite=Strictの Cookie(望ましい)か、Backend-for-Frontend(BFF)のパターンを使う。OAuth 2.0 for Browser-Based Apps を参照する。
範囲
localStorage API で保存したデータは、同じオリジンから読み込まれたページからアクセスできる。
オリジンはスキーム(https://)、ホスト(example.com)、ポート(443)、ドメインや領域(example.com)によって定義される。
これは Cookie の secure フラグを使った場合と似たアクセスを与える。
つまり https から保存したデータは http 経由では取得できない。
別のウィンドウやスレッドから同時にアクセスされうるため、localStorage で保存したデータは共有アクセスの問題(競合状態など)を受けやすく、ロックを持たないものとみなすべきである(Web Storage API の仕様)。
期間
localStorage API で保存したデータは閲覧のセッションをまたいで持続するので、そのシステムの他の利用者からアクセスされうる時間が延びる。
オフラインでのアクセス
標準は localStorage のデータの保存時の暗号化を要求していないので、ディスクから直接そのデータへアクセスできる可能性がある。
用途
WHATWG は、ウィンドウやタブをまたいで、また複数のセッションをまたいでアクセスする必要のあるデータ、そして性能上の理由から大量(数メガバイト)のデータを保存する必要がある場合に localStorage を使うことを示唆している。
sessionStorage API
範囲
sessionStorage API は、呼び出されたウィンドウの文脈内にデータを保存する。
つまりタブ 1 は、タブ 2 から保存されたデータにアクセスできない。
また localStorage API と同じく、sessionStorage API で保存したデータは同じオリジンから読み込まれたページからアクセスできる。
オリジンはスキーム(https://)、ホスト(example.com)、ポート(443)、ドメインや領域(example.com)によって定義される。
これは Cookie の secure フラグを使った場合と似たアクセスを与えるので、https から保存したデータは http 経由では取得できない。
期間
sessionStorage API は、現在の閲覧セッションの期間だけデータを保存する。
そのタブが閉じられれば、そのデータは取得できなくなる。
ブラウザのタブが再利用されたり開いたままにされたりした場合には、必ずしもアクセスを防ぐわけではない。
またガベージコレクションの事象が起こるまで、データがメモリに残ることもある。
オフラインでのアクセス
標準は sessionStorage のデータの保存時の暗号化を要求していないので、ディスクから直接そのデータへアクセスできる可能性がある。
用途
WHATWG は、チケットの予約の詳細のように一つの作業の流れに関係するが、他のタブで複数の流れを同時に行いうるデータに sessionStorage を使うことを示唆している。
ウィンドウやタブに結びつく性質によって、別のタブの作業の流れのあいだでデータが漏れることを防げる。
参考資料
Web Worker
Web Worker は、現在のウィンドウとは別の大域的な文脈で JavaScript のコードを実行する。
主たる実行のウィンドウとの通信路が存在し、それは MessageChannel と呼ばれる。
用途
Web Worker は、ページの再読み込みをまたいだ保存の持続が要件でない場合に、(セッションの)シークレットをブラウザに保存する代替になる。 Web Worker が安全なブラウザ内の保存を提供するには、そのシークレットを必要とするコードすべてが Web Worker の中に存在し、シークレットが主たるウィンドウの文脈へ決して送られない状態にする必要がある。
Web Worker のメモリ内にシークレットを保存することは、HttpOnly の Cookie と同じ安全性の保証を与える。
すなわちシークレットの機密性が保護される。
それでも XSS 攻撃によって Web Worker へメッセージを送り、シークレットを必要とする操作を行わせることはできる。
Web Worker はその操作の結果を主たる実行のスレッドへ返す。
HttpOnly の Cookie と比べた Web Worker の実装の利点は、Web Worker では隔離された一部の JavaScript のコードがシークレットへアクセスできることである。
HttpOnly の Cookie はいかなる JavaScript からもアクセスできない。
フロントエンドの JavaScript のコードがシークレットへのアクセスを必要とするなら、Web Worker の実装はシークレットの機密性を保つ唯一のブラウザ内保存の選択肢である。
セッション ID の生存期間
セッション ID の生成と検証。寛容なセッション管理と厳格なセッション管理
Web アプリケーションのセッション管理の仕組みには、セッション固定の脆弱性に関して寛容なものと厳格なものの二種類がある。 寛容な仕組みでは、利用者が設定したどのセッション ID の値も最初は正当なものとして受け付け、それに対して新しいセッションを作る。 厳格な仕組みでは、その Web アプリケーションが以前に生成したセッション ID の値のみを受け付けることを強制する。
セッションのトークンは、可能であれば Web サーバーが扱うか、暗号論的に安全な乱数生成器で生成するべきである。
今日最も一般的に使われているのは(より安全な)厳格な仕組みであるが、PHP は既定で寛容である。 開発者は、特定の状況で Web アプリケーションが寛容な仕組みを使わない状態にしなければならない。 Web アプリケーションは自身が生成したことのないセッション ID を決して受け付けるべきではなく、受け取った場合は新しい正当なセッション ID を生成して利用者へ与えるべきである。 加えて、この状況は疑わしい活動として検出され、警報が生成されるべきである。
セッション ID を他の利用者入力と同様に扱う
セッション ID は、Web アプリケーションが処理する他の利用者入力と同じく信頼できないものとみなし、徹底的に検証しなければならない。 使うセッション管理の仕組みに応じて、セッション ID は GET や POST のパラメータ、URL、あるいは HTTP ヘッダ(Cookie など)で受け取られる。 Web アプリケーションが不正なセッション ID の値を処理の前に検証して除外しないと、他の Web の脆弱性の悪用に使われうる。 セッション ID がリレーショナルデータベースに保存されているなら SQL インジェクション、保存されて後に Web アプリケーションによって出力されるなら持続型 XSS である。
権限の水準が変わったらセッション ID を更新する
紐づく利用者セッションにおいて権限の水準が変わったあとは、Web アプリケーションがセッション ID を更新(再生成)しなければならない。 セッション ID の再生成が必須になる最も一般的な場面は認証の処理である。 利用者の権限の水準が、認証されていない(匿名の)状態から認証された状態へ変わる(場合によってはまだ認可された状態ではない)。 考慮すべき一般的な場面には、パスワードの変更、権限の変更、Web アプリケーション内で通常の利用者の役割から管理者の役割へ切り替わることが含まれる。 Web アプリケーションのすべての機微なページでは、以前のセッション ID は無視されなければならず、保護されたリソースへの新しいリクエストには現在のセッション ID のみを割り当てなければならず、古い(以前の)セッション ID は破棄しなければならない。
最も一般的な開発フレームワークは、セッション ID を更新するセッションの関数やメソッドを提供している。
request.getSession(true) と HttpSession.invalidate()(J2EE)、Session.Abandon() と Response.Cookies.Add(new...)(ASP .NET)、session_start() と session_regenerate_id(true)(PHP)などである。
セッション ID の再生成はセッション固定攻撃を防ぐために必須である。 そこでは攻撃者が、他のセッションに関する攻撃の多くのように被害者のセッション ID を集めるのではなく、被害者のブラウザにセッション ID を設定する。 HTTP と HTTPS のどちらを使っていても関係なく起こる。 この保護は、HTTP レスポンス分割や XSS のようにセッション固定攻撃の実行にも使える他の Web の脆弱性の影響も緩和する(こちらとこちらを参照)。
補完的な推奨として、認証の前後で異なるセッション ID やトークンの名前(あるいはセッション ID の集合)を使うとよい。 そうすれば、両方の状態のあいだで利用者セッションを露出させたり結びつけたりするリスクなしに、Web アプリケーションが匿名の利用者と認証された利用者を追跡できる。
リスク事象のあとの再認証
Web アプリケーションは、次のような高リスクの事象のあとに再認証を求めるべきである。
- 重要な利用者情報(パスワード、メールアドレスなど)の変更
- 新しい、あるいは疑わしい IP アドレスや端末からのログイン試行
- アカウントの復旧の流れ(パスワードの再設定、アカウント侵害の検知など)
これらの事象のあとに再認証を実装する最良の慣行については、認証のチートシートのリスク事象のあとの再認証の節を参照する。
追加の資料
- Tailscale による Why Frequent Reauthentication Can Be a UX Pitfall
複数の Cookie を使うときの考慮点
Web アプリケーションがセッション ID の交換の仕組みとして Cookie を使い、あるセッションに複数の Cookie が設定される場合、利用者セッションへのアクセスを許す前に、そのすべての Cookie を検証し、それらのあいだの関係を強制しなければならない。
認証されていない(匿名の)利用者を追跡するために、認証前の利用者の Cookie を HTTP で設定することは Web アプリケーションで非常によく行われる。 利用者が認証されると、認証後の新しい安全な Cookie が HTTPS で設定され、両方の Cookie と利用者セッションのあいだに結びつけが確立される。 認証済みのセッションについて Web アプリケーションが両方の Cookie を検証しなければ、攻撃者は認証前の保護されていない Cookie を使って認証済みの利用者セッションへアクセスできる(こちらとこちらを参照)。
Web アプリケーションは、同じアプリケーション内の異なるパスやドメインの範囲に対して同じ Cookie の名前を使うことを避けるべきである。 それは仕組みの複雑さを増し、範囲に関する問題を持ち込みうる。
セッションの失効
攻撃者が有効なセッションに対して攻撃を仕掛け、それを乗っ取れる期間を最小にするには、すべてのセッションに失効のタイムアウトを設定し、セッションが有効なままである時間を定めることが必須である。 Web アプリケーションによるセッションの失効が不十分だと、他のセッションに関する攻撃への露出が増える。 攻撃者が正当なセッション ID を再利用して紐づくセッションを乗っ取るには、それがまだ有効でなければならないからである。
セッションの間隔が短いほど、攻撃者が正当なセッション ID を使える時間は短くなる。 セッションの失効のタイムアウトの値は、その Web アプリケーションの目的と性質に応じて設定し、安全性と使いやすさの釣り合いをとらなければならない。 利用者がセッションを頻繁に失効させられることなく、快適にアプリケーション内の操作を完了できる状態にする。
無操作のタイムアウトと絶対のタイムアウトの値は、どちらもその Web アプリケーションとそのデータの重要性に強く依存する。 無操作のタイムアウトの一般的な範囲は、価値の高いアプリケーションで 2 分から 5 分、リスクの低いアプリケーションで 15 分から 30 分である。 絶対のタイムアウトは、利用者が通常どれだけの時間そのアプリケーションを使うかによる。 事務職の利用者が一日中使うことを想定したアプリケーションであれば、4 時間から 8 時間が適切な範囲になりうる。
セッションが失効したとき、Web アプリケーションはクライアントとサーバーの両側でセッションを無効化する能動的な措置を採らなければならない。 安全性の観点からは、後者が最も重要で必須である。
ほとんどのセッションの交換の仕組みでは、セッション ID を無効化するクライアント側の措置はトークンの値を消すことに基づく。
たとえば Cookie を無効化するには、セッション ID に空(または不正)の値を与え、Expires(または Max-Age)の属性を過去の日付に設定することが推奨される(持続する Cookie が使われている場合のため)。
Set-Cookie: id=; Expires=Friday, 17-May-03 18:45:00 GMT のようにする。
サーバー側でセッションを閉じて無効化するには、セッションが失効したとき、あるいは利用者が能動的にログアウトしたときに、Web アプリケーションがセッション管理の仕組みが提供する関数やメソッドを使って能動的な措置を採ることが必須である。
HttpSession.invalidate()(J2EE)、Session.Abandon()(ASP .NET)、session_destroy() と unset()(PHP)などである。
自動のセッション失効
無操作のタイムアウト
すべてのセッションは無操作(非活動)のタイムアウトを実装するべきである。 このタイムアウトは、セッションに活動がない場合にそれが有効なままである時間を定め、あるセッション ID に対して Web アプリケーションが最後に HTTP リクエストを受け取ってから定められた無操作の期間が経過した時点で、セッションを閉じて無効化する。
無操作のタイムアウトは、攻撃者が他の利用者の正当なセッション ID を推測して使う機会を限定する。 ただし攻撃者があるセッションを乗っ取れた場合、無操作のタイムアウトは攻撃者の行動を制限しない。 セッションに定期的に活動を生じさせ、より長い期間セッションを有効に保てるからである。
セッションのタイムアウトの管理と失効はサーバー側で強制しなければならない。 クライアントがセッションのタイムアウトを強制するために、セッションのトークンやその他のクライアントのパラメータで時刻の基準(ログインからの経過分数など)を追跡している場合、攻撃者はそれを操作してセッションの期間を延ばせる。
絶対のタイムアウト
すべてのセッションは、セッションの活動に関わらず絶対のタイムアウトを実装するべきである。 このタイムアウトは、セッションが有効でありうる最大の時間を定め、Web アプリケーションがそのセッションを最初に作成してから定められた絶対の期間が経過した時点で、セッションを閉じて無効化する。 セッションの無効化のあと、利用者はそのアプリケーションで再度認証を行い、新しいセッションを確立することを強いられる。
絶対のセッションの制限は、攻撃者が乗っ取ったセッションを使って被害者になりすませる時間の量を制限する。
更新のタイムアウト
あるいは Web アプリケーションは、追加の更新のタイムアウトを実装できる。 その経過後、利用者のセッションの途中で、セッションの活動(したがって無操作のタイムアウト)とは独立に、セッション ID が自動的に更新される。
セッションが最初に作られてから特定の時間が経過したあと、Web アプリケーションは利用者セッションに新しい ID を再生成し、それをクライアントに設定または更新しようとする。 以前のセッション ID の値も、安全のための間隔をとって、クライアントが新しい ID を認識してそれを使い始めるまでの一定期間は有効なままとなる。 その時点で、クライアントが現在のセッション内で新しい ID へ切り替えると、アプリケーションは以前の ID を無効化する。
これは、攻撃者が入手した可能性のあるセッション ID の値が、被害者のセッションがまだ有効な場合でも、利用者セッションの乗っ取りに再利用されうる時間の量を最小にする。 利用者セッションは正当なクライアント上で生き続け開いたままであるが、それに紐づくセッション ID の値は、更新のタイムアウトが経過するたびに、セッションの期間中に意識されない形で定期的に更新される。 したがって更新のタイムアウトは無操作のタイムアウトと絶対のタイムアウトを補完する。 とくに絶対のタイムアウトの値が長期間に及ぶ場合(利用者セッションを長時間開いたままにすることがアプリケーションの要件である場合など)に有効である。
実装によっては、更新のタイムアウトが経過した直後に、まだ有効な以前のセッション ID を持つ攻撃者が被害者より先にリクエストを送り、更新されたセッション ID の値を先に得てしまう競合状態が生じうる。 少なくともこの場面では、紐づくセッション ID がもはや有効でないためにセッションが突然終了するので、被害者は攻撃に気付くかもしれない。
手動のセッション失効
Web アプリケーションは、安全性を意識する利用者が、その利用を終えた時点で能動的にセッションを閉じられる仕組みを提供するべきである。
ログアウトのボタン
Web アプリケーションは、目に見えて容易にアクセスできるログアウト(ログオフ、終了、セッションを閉じる)のボタンを提供しなければならない。 それはアプリケーションのヘッダやメニューで使えて、すべてのリソースとページから到達できるものとし、利用者がいつでも手動でセッションを閉じられる状態にする。 セッションの失効の節で述べたように、Web アプリケーションは少なくともサーバー側でセッションを無効化しなければならない。
注記:残念ながら、すべての Web アプリケーションが利用者による現在のセッションの終了を容易にしているわけではない。したがって、クライアント側の補強によって、注意深い利用者が自身のセッションを丁寧に閉じる助けを得られる。
Web の内容のキャッシュと Clear-Site-Data
セッションが終わったあとでも、その最中に交換された非公開または機微なデータが、ブラウザのキャッシュを通じてなおアクセスできることがある。
これを緩和するため、Web アプリケーションはすべての HTTP と HTTPS の通信に対して制限的なキャッシュの指示を使わなければならない。
Cache-Control や Pragma のような HTTP ヘッダ、あるいは同等の <meta> タグを、すべてのページ、とくに機微な内容を表示するページで使うことを含む。
セッション識別子は決してキャッシュされてはならない。
これを防ぐため、セッション ID を含む応答に Cache-Control: no-store の指示を含めることが強く推奨される。
キャッシュを許して再検証を求める no-cache と違い、no-store はその応答(Set-Cookie のようなヘッダを含む)がいかなるキャッシュにも保存されないことを確かにする。
将来のキャッシュを防ぐことに加えて、アプリケーションはセッションが終わるときに、既に保存された機微なデータが除去される状態にするべきである。
これは、ログアウトやセッションの終了の際に Clear-Site-Data の応答ヘッダを返す(Clear-Site-Data: "cache", "cookies", "storage" など)ことで実現できる。
これはブラウザに、そのオリジンに紐づくキャッシュされたリソース、Cookie、その他のクライアント側の保存を削除するよう指示し、セッションの完全な後片付けの助けになる。
注記:セッション ID のキャッシュを防ぐために
Cache-Control: no-cache="Set-Cookie, Set-Cookie2"の指示が示されることがある。しかしこの構文は広く対応されておらず、意図しない挙動につながりうる。より強い保護のためには代わりにCache-Control: no-storeを使う。Clear-Site-Data: cacheはブラウザのキャッシュにあるそのサイトの保存済みの応答をすべて消せるので、注意して使う。これは共有キャッシュや中間のキャッシュには影響しないことに注意する。 参考:MDN の Cache-Control と MDN の Clear-Site-Data ヘッダ
リスク事象のあとの再認証
セッションの完全性とアカウントの保護を確かにするため、アプリケーションは特定の高リスクの事象が検知されたときに再認証を求めるべきである。 次のものが含まれうる。
- パスワード変更の試行または完了
- 新しい、あるいは疑わしい IP アドレスや端末からのログイン
- アカウントの復旧やチャレンジの流れの完了
再認証を求めることは、セッションの乗っ取りと不正なアクセスの緩和に役立つ。 とくに長期間有効なセッションや外部のアイデンティティプロバイダを使っている場合に有効である。
推奨される慣行
- 主要な資格情報(パスワードなど)を求めるか、MFA を強制する
- 再認証が必要な理由を説明する明確なメッセージを示す
セッション管理のためのクライアント側の追加の防御
Web アプリケーションは、これまで述べたセッション管理の防御を、クライアント側の追加の対策で補完できる。 クライアント側の保護は通常 JavaScript による確認と検証の形をとり、鉄壁ではなく熟練した攻撃者に容易に破られうるが、侵入者が回避しなければならない防御の層をもう一つ持ち込める。
最初のログインのタイムアウト
Web アプリケーションはログインページで JavaScript のコードを使い、そのページが読み込まれセッション ID が与えられてからの時間を評価し測れる。 特定の時間が経過したあとにログインが試みられた場合、クライアント側のコードはログインに許される最大の時間が過ぎたことを利用者へ知らせ、ログインページを再読み込みして新しいセッション ID を取得できる。
この追加の保護の仕組みは、認証前のセッション ID の更新を強制しようとするものである。 たとえばセッション固定攻撃において、同じコンピュータを使う次の被害者が、以前に使われた(あるいは手動で設定された)セッション ID を再利用してしまう場面を避ける。
ブラウザのウィンドウを閉じる事象でセッションのログアウトを強制する
Web アプリケーションは JavaScript のコードを使って、ブラウザのタブやウィンドウを閉じる(あるいは戻る)事象をすべて捕まえ、ブラウザを閉じる前に現在のセッションを閉じる適切な措置を採れる。 利用者がログアウトのボタンで手動でセッションを閉じたのと同じ状態を模倣する。
ブラウザのタブをまたぐセッションを無効にする
Web アプリケーションは、利用者がログインしてセッションが確立されたあと、同じアプリケーションに対して新しいタブやウィンドウが開かれた場合に再認証を強制する JavaScript のコードを使える。 そのアプリケーションは、複数のタブやウィンドウが同じセッションを共有することを許したくない。 したがって、ブラウザがそれらのあいだで同時に同じセッション ID を共有しないよう強制しようとする。
注記:セッション ID が Cookie で交換される場合、この仕組みは実装できない。Cookie はブラウザのすべてのタブやウィンドウで共有されるからである。
クライアント側の自動ログアウト
Web アプリケーションは、すべての(あるいは重要な)ページで JavaScript のコードを使い、無操作のタイムアウトが経過したあとにクライアントのセッションを自動的にログアウトできる。 たとえば利用者をログアウトのページ(前述のログアウトのボタンが使うのと同じリソース)へリダイレクトする。
サーバー側の無操作のタイムアウトの機能をクライアント側のコードで補強する利点は、利用者が非活動によってセッションが終わったことを目にできること、あるいは残り時間の表示と警告のメッセージによってセッションが失効しようとしていることを事前に知らせられることである。 この利用者に親切な方法は、サーバー側でセッションが黙って失効したことによって、入力の多いページで作業を失うことを避ける助けになる。
セッションに対する攻撃の検出
セッション ID の推測と総当たりの検出
攻撃者が正当なセッション ID を推測または総当たりしようとする場合、単一の(あるいは複数の)IP アドレスから、異なるセッション ID を使って標的の Web アプリケーションに対して複数の連続したリクエストを送る必要がある。 加えて攻撃者がセッション ID の予測可能性を(統計的な解析などで)分析しようとする場合、新しい正当なセッション ID を集めるために、単一の(あるいは複数の)IP アドレスから標的の Web アプリケーションに対して複数の連続したリクエストを送る必要がある。
Web アプリケーションは、異なるセッション ID を集める(あるいは使う)試行の回数に基づいて、この両方の場面を検出でき、問題のある IP アドレスに警報を出すか遮断できなければならない。
セッション ID の異常の検出
Web アプリケーションは、セッション ID に紐づく異常、たとえばその操作の検出に焦点を置くべきである。 OWASP AppSensor プロジェクトは、検出の地点と対応の動作という形で、異常と想定外の振る舞いの検出に焦点を置いた侵入検知の機能を Web アプリケーションに組み込むための枠組みと方法論を提供する。 外部の保護の層を使うのではなく、業務ロジックの詳細と高度な判断材料が Web アプリケーションの内部からしか得られない場合がある。 そこでは、既存の Cookie が変更または削除されたとき、新しい Cookie が追加されたとき、他の利用者のセッション ID が再利用されたとき、セッションの途中で利用者の位置や User-Agent が変わったときなど、セッションに関する複数の検出の地点を置ける。
セッション ID を他の利用者の属性に結びつける
利用者の不適切な振る舞いとセッションの乗っ取りを検出(場合によっては防御)する目的で、セッション ID をクライアントの IP アドレス、User-Agent、クライアント側のデジタル証明書といった他の利用者やクライアントの属性に結びつけることが強く推奨される。 確立されたセッションの途中でこれらの属性のあいだに変化や異常を Web アプリケーションが検出した場合、それはセッションの操作と乗っ取りの試みを示す非常に良い手がかりであり、この単純な事実を使って疑わしいセッションに警報を出すか終了させられる。
これらの属性は、セッションに対する攻撃を信頼して防ぐために Web アプリケーションが使えるものではないが、検出(と保護)の能力を大きく高める。 ただし熟練した攻撃者は、同じネットワークを共有して被害者に割り当てられた同じ IP アドレスを再利用する(Wi-Fi のアクセスポイントのような NAT の環境で非常によくある)、同じ外向きの Web プロキシを使う(企業の環境で非常によくある)、あるいは User-Agent を被害者とまったく同じに手作業で書き換えることで、これらの対策を回避できる。
セッションの生存期間の記録。セッション ID の作成、利用、破棄の監視
Web アプリケーションは、セッションの生存期間の全体に関する情報を含めることでログの能力を高めるべきである。 とくに、セッション ID の作成、更新、破棄といったセッションに関する事象、ログインとログアウトの操作におけるその利用の詳細、セッション内の権限の水準の変化、タイムアウトの失効、(検出された場合の)不正なセッションの活動、そしてセッション中の重要な業務操作を記録することが推奨される。
ログの詳細には、タイムスタンプ、送信元 IP アドレス、要求された(セッションの操作に関わる)Web の対象リソース、HTTP ヘッダ(User-Agent と Referer を含む)、GET と POST のパラメータ、エラーコードとメッセージ、ユーザー名(利用者 ID)、そしてセッション ID(Cookie、URL、GET、POST など)を含められる。
セッション ID のような機微なデータは、セッションのログをセッション ID のローカルやリモートでの露出や不正なアクセスから守るため、ログに含めるべきではない。 しかしログの項目を特定のセッションに対応づけるためには、何らかのセッション固有の情報を記録しなければならない。 セッション ID を露出させずにセッション単位でのログの対応づけを可能にするため、セッション ID そのものではなくソルト付きのハッシュを記録することが推奨される。
とくに Web アプリケーションは、現在有効なすべてのセッションを管理できる管理用のインタフェースを徹底的に保護しなければならない。 これはしばしば、サポート担当者がセッションに関する問題、あるいは一般的な問題を、利用者になりすまして利用者と同じようにアプリケーションを見ることで解決するために使われる。
セッションのログは Web アプリケーションの侵入検知の主要なデータ源の一つとなり、(一つまたは複数の)攻撃が検知されたときにセッションを自動的に終了させたり利用者のアカウントを無効にしたりするため、侵入防御のシステムにも使える。 能動的な保護を実装する場合は、その防御の動作も記録しなければならない。
同時のセッションのログオン
同じ利用者による複数の同時のログオンを、同じクライアント IP アドレスから、あるいは異なる IP アドレスから許すかどうかは、Web アプリケーションの設計上の判断である。 同時のセッションのログオンを許したくない場合、Web アプリケーションは新しい認証の事象のたびに実効的な措置を採らなければならない。 以前から使えていたセッションを暗黙に終了させるか、(古い、新しい、あるいは両方のセッションを通じて)どのセッションを有効なままにするかを利用者に尋ねる。
Web アプリケーションには、利用者がいつでも有効なセッションの詳細を確認でき、同時のログオンを監視して利用者へ警報を出し、セッションを手動でリモートから終了させる機能を与え、IP アドレス、User-Agent、ログインの日時、無操作の時間など複数のクライアントの詳細を記録してアカウントの活動履歴(記録簿)を追跡する機能を加えることが推奨される。
セッション管理における WAF による保護
Web アプリケーションのソースコードが手に入らない、あるいは変更できない場合や、上述の複数の安全性の推奨と最良の慣行を実装するために必要な変更がアプリケーションのアーキテクチャの全面的な再設計を意味し、したがって短期的には容易に実装できない場合がある。
こうした場面では、あるいは Web アプリケーションの防御を補完し、アプリケーションを可能なかぎり安全に保つ目的で、既に述べたセッション管理の脅威を緩和できる Web アプリケーションファイアウォール(WAF)のような外部の保護を使うことが推奨される。
Web アプリケーションファイアウォールは、セッションに関する攻撃に対する検知と保護の機能を提供する。
一方では、新しい Cookie を設定するすべての応答に対して Set-Cookie ヘッダへ基本的な書き換えの規則を適用することで、Secure や HttpOnly のフラグのような Cookie の安全性の属性の使用を WAF が強制することは容易である。
他方では、WAF がセッションと対応するセッション ID を追跡し、あらゆる種類の保護を適用できるより高度な機能も実装できる。 セッション固定への対策(権限の変化が検知されたときにクライアント側でセッション ID を更新する)、固定的なセッションの強制(セッション ID と IP アドレスや User-Agent のような他のクライアントの属性との関係を検証する)、セッションの失効の管理(クライアントと Web アプリケーションの両方にセッションを終了させる)である。
オープンソースの ModSecurity WAF と OWASP の Core Rule Set は、Cookie の安全性の属性を検知し適用する機能、セッション固定攻撃への対策、そして固定的なセッションを強制するセッション追跡の機能を提供する。