プロンプトと適用されるコンテキスト
2つのリージョンにまたがるB2B Webアプリケーションのセッション管理を設計してください。ブラウザは通常アクセスおよび管理者アクセスに サーバーサイドセッションを使用します。このプロダクトには、現在のデバイスでのログアウト、全デバイスでのログアウト、 およびパスワード変更後の失効が必要です。グローバルログアウトは、両方のリージョンで保護されたリクエストを 5秒以内に拒否する必要があります。
この演習では、256ビットの不透明な識別子(opaque identifier)、30分のアイドルタイムアウト、12時間の絶対 タイムアウトを選択します。これらはシナリオ上の決定事項であり、普遍的なセキュリティ定数ではありません。ログイン、 管理者への昇格、有効期限切れ、ログアウト、および盗難が疑われるイベント時に何が変化するかを説明してください。Cookie属性のみを 説明するのではなく、同時リクエストやリージョン間の伝播も含めてください。
これはバックエンドのライフサイクルに関する質問です。優れた設計は、ブラウザトークンを無意味なものに保ち、 サーバーを認証状態のオーソリティとし、信頼境界の変更全体でアイデンティティをローテーションし、 古い認証情報が機能しなくなることを証明できます。Secure、HttpOnly、SameSiteは重要ですが、これら単体で サーバーサイドの有効期限、認可、または失効を提供できるものはありません。
面接官が評価するポイント
第1のシグナルは、候補者がセッション識別子を一時的なベアラ認証情報として扱っているかどうかです。 識別子は予測不可能であり、意図された1つのメカニズムを介して受け入れられ、転送時および保存時に保護され、 URLやログに含まれていてはなりません。データベースやログの閲覧者が、使用可能なトークンを自動的に取得できてはなりません。
第2のシグナルは、ライフサイクルの推論です。匿名識別子が単に認証済みのものへと昇格してはなりません。 ログインや権限昇格は信頼境界を越えるため、アプリケーションは新しい識別子を作成し、 古い識別子を破棄します。アイドル有効期限と絶対有効期限はサーバーによって強制されます。ブラウザCookieのクリアは クライアント側のクリーンアップにすぎず、攻撃者がすでに保持しているコピーを失効させるものではありません。
第3のシグナルは、認証と認可の区別です。有効なセッションは、ユーザーと 認証コンテキストを特定します。すべてのリクエストは、現在のテナントメンバーシップと権限を引き続き確認します。 無効化ルールなしにロールを長期間存続するセッションレコードにコピーすると、管理者がロールを削除した後も アクセス権が維持されてしまう可能性があります。
第4のシグナルは、分散整合性です。グローバルログアウトが5秒以内に有効になるという保証には、 信頼できるバージョンまたは失効状態、制限されたキャッシュの鮮度、およびパーティション発生時の 動作が必要です。別のリージョンがキャッシュされた肯定的な結果を使用し続けられる場合、「Redisから削除する」というだけでは不完全です。
最後のシグナルは検証です。候補者は、固定化、盗聴、CSRF、有効期限切れ、同時 ローテーション、レプリカラグ、ログ漏洩を実行可能なテストケースに落とし込む必要があります。セキュリティ上の主張は、 各古い識別子に対して、それを使用したリクエストが必ず失敗しなければならない特定のイベントが定義されているときに初めて信頼できるものになります。
回答前に明確にすべき質問
- どのクライアントが対象ですか? この回答は同一サイトのブラウザアプリケーションを対象としています。ネイティブアプリや
サードパーティのAPIクライアントは通常、異なるトークン転送およびライフサイクルを必要とします。
- 「5秒以内」とはどういう意味ですか? サーバーはその時間までに保護されたリクエストを拒否しなければなりません。ブラウザの
タブは、次のリクエストが発生するまで古いコンテンツを表示し続ける可能性があります。
- 同時セッションは許可されますか? この設計では複数のデバイスを許可し、各セッションを
個別に保存します。より厳格なプロダクトでは、セッション数を制限したり、ログイン時に古いセッションを置き換えたりすることができます。
- どのようなイベントがすべてを失効させますか? グローバルログアウトとパスワード変更により、アカウント全体でのセッションエポックが
インクリメントされます。権限の変更は、すべてのデバイスからログアウトさせることなく認可バージョンを インクリメントできます。
- 管理者アクセスにはどの程度のリスクがありますか? この設計では昇格時にローテーションを行い、認証強度を
記録します。影響の大きい操作には、最近の再認証が要求される場合もあります。
- クロスサイトログインや埋め込みは機能する必要がありますか?
SameSite=Laxは、想定されるファーストパーティナビゲーションに適しています。
正当なクロスサイトフローには、限定的な例外と明示的なCSRF保護が必要です。
- リージョン間のパーティション発生時には何が起こりますか? 5秒のセキュリティ要件は、最新の失効状態を
取得できない場合に、高リスクなルートに対してfail-closed(安全側に倒して拒否)の動作をすることを意味します。
30秒の回答フレームワーク
「私は256ビットの不透明な値を__Host- Cookieに入れ、Secure、HttpOnly、Path=/、および 明示的なSameSiteポリシーを設定し、サーバー上にはHMACから導出されたルックアップキーのみを保存します。セッションレコードには、 ユーザー、テナント、認証強度、作成日時とアクティビティ日時、有効期限、およびユーザーの セッションバージョンと認可バージョンが含まれます。ログインおよび管理者への昇格時には、アトミックに新しいセッションを発行し、 古い識別子を無効化します。保護されたすべてのリクエストは、アイドルおよび絶対有効期限、現在のアカウントエポック、 および現在の認可を強制します。ログアウト時は該当行を失効させ、全デバイスログアウトまたはパスワード変更時は アカウントエポックをインクリメントし、5秒のキャッシュ上限を設けて両リージョンに無効化をパブリッシュします。固定化、 古いIDの再利用、同時ローテーション、CSRF、タイムアウト境界、リージョン間の遅延、ログの秘匿化を検証します。」
ステップバイステップの詳細解説
脅威モデルから始めます。攻撃者は、被害者がログインする前に識別子を設定したり、ブラウザや インフラストラクチャから識別子を盗んだり、別のデバイスからリプレイしたり、セッションを維持し続けたり、古い権限を悪用したり、 ローテーションと競合させたりする可能性があります。また、ブラウザは自動的にCookieを送信するため、システムは クロスサイトの状態変更リクエストにも耐性を持つ必要があります。
独自に乱数生成器やパーサーを自作するのではなく、フレームワークでレビュー済みのセッション実装を使用します。 本設計では、暗号学的に安全な生成器を使用して32バイトの乱数を生成します。生の 不透明な値はCookieでのみ送信します。ストレージにクエリを実行する前に、HMAC-SHA-256(server_key, raw_id)などの 固定長ルックアップキーを導出することで、データベースのスナップショットに ベアラ値が含まれないようにします。そのHMACのキーローテーションには、明示的なデュアルリード移行計画が必要です。
サーバーレコードの例は以下のとおりです。
session_lookup_key
user_id, tenant_id
created_at, last_seen_at
idle_expires_at, absolute_expires_at
authentication_time, authentication_strength
session_epoch, authorization_version
revoked_at, revocation_reasonブラウザは以下のようなホストオンリーCookieを受け取ります。
Set-Cookie: __Host-session=<opaque>; Secure; HttpOnly; SameSite=Lax; Path=/__Host-プレフィックスはSecureを要求し、Domainを省略し、Path=/を使用するため、サブドメインによるCookieインジェクションを 低減します。HttpOnlyはJavaScriptからの直接読み取りをブロックしますが、注入されたスクリプトによる 認証済みアクションの実行を防ぐことはできません。SameSiteは一部のクロスサイトリクエストを抑制しますが、多層防御の一部です。状態を変更する ルートでは、依然としてCSRFトークンまたは他のリクエストにバインドされた証明を使用し、必要に応じてOriginを検証します。
セッションはCookieからのみ受け入れます。別のクライアントプロトコルで明示的に定義されていない限り、 URLパラメータや代替ヘッダーを介して提供された識別子は拒否します。URLは履歴、リファラー、 アナリティクス、スクリーンショット、プロキシログを通じて漏洩します。トークンの構文を検証し、一定の処理時間で失敗を 処理するとともに、無効な識別子の繰り返しに対してレート制限を設けます。
匿名状態と認証済み状態は分離しておきます。認証情報が成功したら、短いトランザクション内で新しい 認証済みセッションを作成し、ログイン前の識別子を無効化します。管理者への昇格や その他の権限拡大時には、適切な証明を要求し、別の新しい識別子を作成して、 より低い信頼レベルの識別子を無効化します。レスポンスで新しいCookieが設定されるのは、サーバー状態がコミットされた後のみです。
ブラウザの並列リクエストにより、ローテーションは複雑になります。古い識別子が即座に破棄された場合、 処理中のリクエストが不正なレスポンスを受け取る可能性があります。制限された猶予期間を設けて、古い識別子を すでに作成された後続の識別子に数秒間マッピングすることは可能ですが、複数の後続識別子を生成したり、任意のリプレイに対して 新しいベアラ値を返したりしてはなりません。1つのアトミックなローテーションレコードを使用し、後続の識別子は 正当なレスポンスを通じてのみ利用できるようにします。機密性の高い昇格の場合、広い猶予期間を設けるよりも、短い再試行を受け入れる方が安全です。
保護されたすべてのリクエストにおいて、セッションを検索し、失効した行を拒否し、サーバー時間を使用して両方の 有効期限クロックを強制し、session_epochを現在のアカウントエポックと比較します。30分のアイドルタイムアウトは 意味のあるアクティビティでのみ延長され、リクエストごとの書き込みを避けるためにバケット単位で更新できます。12時間の 絶対期限は決して延長されません。クライアント側のカウントダウンはユーザビリティを向上させますが、有効性を決定するものではありません。
次に、現在のテナントメンバーシップと認可状態をロードするか、無効化契約が明示的なバージョンを 比較します。有効なセッションであっても、オブジェクトレベルの認可を置き換えることはありません。IPアドレス、ネットワーク、 デバイス、User-Agentの変更は有用なリスクシグナルです。これらに厳密にバインドすると、モバイルネットワーク、プロキシ、 共有デバイスの背後で誤ったログアウトが発生します。高リスクな変更は、ポリシーに従って再認証をトリガーするか、セッションを 失効させることができます。
現在のデバイスのログアウトは、Cookieを失効させる前にそのセッションをアトミックに失効済みとしてマークします。全デバイスログアウトおよび パスワード変更はユーザーのsession_epochをインクリメントします。これにより、個別の行が 残っていたとしても、それ以前のすべてのセッションが無効になります。新しいエポックを両方のリージョンにパブリッシュし、肯定的なキャッシュを無効化します。キャッシュの有効期間は 最大5秒に制限し、キャッシュが古い場合、管理者などの高リスクなルートではオーソリティ状態から読み取らせます。パーティション発生時、 これらのルートはfail-closedになります。可用性が定められた失効保証をオーバーライドすることはできないためです。
測定することなく5秒の期限を約束してはなりません。オーソリティ側のコミット時間と、 各リージョンで最初に拒否が観測された時間を記録します。伝播が上限に近づいた場合にアラートを発報します。セッションの 作成、ローテーション、昇格、期限切れ、失効を秘密情報ではない相関IDで監査します。生Cookie、 ルックアップキー、完全なCookieヘッダーは決してログに記録しないでください。
状態遷移を中心にテストを構築します。攻撃者が選択した匿名Cookieを設定してログインし、アカウントにアクセスできないことを 証明します。ログイン前、昇格前、ログアウト済み、期限切れ、パスワード変更前の各IDをリプレイします。 制御されたサーバークロックを使用して、正確なアイドルおよび絶対境界を検証します。2つの昇格リクエストの 競合、1つのリージョンでの無効化遅延、パーティションのシミュレーション、クロスサイトリクエストの送信、そしてすべての アプリケーション、プロキシ、トレーシング、アナリティクス、サポートログからベアラ値がないかをスキャンします。
質の高い回答例
「私はセッションを、ブラウザのハンドルが一時的なベアラ認証情報であるサーバーサイドの状態遷移マシンとしてモデル化します。 このシナリオでは、ハンドルは32バイトの乱数です。Cookieはhost-only、secure、HTTP-only、 path-wide、明示的なSameSite=Laxであり、セッションストアはHMACから導出されたルックアップキーのみを受け取ります。
レコードには、ユーザーとテナント、認証強度、作成日時と最終アクティビティ日時、 30分のアイドル期限、固定の12時間絶対期限、およびアカウントのセッションエポックと認可バージョンのスナップショットが 含まれます。すべてのリクエストで、行、両方の期限、現在のエポック、および現在の認可を検証します。IPやデバイスの変更は、 脆弱な本人確認としてではなく、リスク判定の情報として活用します。
ログインと管理者への昇格では、それぞれ新しいセッションを作成し、より低い信頼レベルの識別子を無効化します。 ローテーションはアトミックであるため、並列リクエストが競合する後続識別子を作成することはありません。ログアウト時はまず サーバーの行を失効させ、その後Cookieをクリアします。全デバイスログアウトおよびパスワード変更時はアカウントエポックをインクリメントし、 キャッシュ無効化をパブリッシュします。両リージョンで肯定的なキャッシュの有効期限を5秒に制限し、高リスクなルートは 状態をリフレッシュできない場合にfail-closedとします。
最後に、固定化、過去のすべての識別子のリプレイ、CSRF、同時ローテーション、アイドルおよび 絶対境界、認可の変更、リージョン間の伝播、パーティション時の動作、秘密情報を含まないログのテストを実施します。重要な 証明は、信頼を変更する各イベントに定義された古い認証情報が存在し、その認証情報が確実に拒否される測定ポイントがあることです。」
よくある間違い
- ログイン後も同じIDを維持する → 攻撃者が事前にIDを選択して認証を待つことができる →
新しい認証済みセッションを発行し、匿名セッションを破棄する。
- ログアウト時にCookieのみをクリアする → 盗まれたコピーが有効なまま残る → **クライアント状態を
クリアする前にサーバー状態を失効させる。**
HttpOnlyをXSS対策として扱う → 注入されたコードは認証済みリクエストを送信可能 →
XSSを防止し、認可およびCSRF防御を独立して強制する。
SameSiteを唯一のCSRF対策として使用する → 正当な例外やブラウザの動作により前提が崩れる →
状態変更にはリクエストにバインドされたCSRF証明を使用する。
- IDをURLに含める → 履歴、リファラー、ログに認証情報がコピーされる → **意図された
Cookieメカニズムを介してのみ受け入れる。**
- アイドル有効期限のみを更新する → アクティブな盗難が永続する可能性がある → 固定の絶対期限を強制する。
- 有効なセッションを無期限にキャッシュする → グローバルログアウトが制限時間を満たせなくなる → **セッションをバージョニングし、
肯定的なキャッシュを制限またはバイパスする。**
- ロールをセッションに永続的に埋め込む → 削除された権限が存続してしまう → **現在の認可情報、
または意図的に無効化されたバージョンを確認する。**
- IPアドレスに厳格にバインドする → 通常のネットワーク変更でユーザーがサインアウトされる → **コンテキストの変更は
リスクシグナルとして使用する。**
- ローテーションに広い猶予期間を設ける → 2つのベアラが有効なまま残る → **アトミックで狭く制限された
ハンドオフを使用するか、機密性の高い昇格では再試行を受け入れる。**
- デバッグのためにCookieヘッダーをログに記録する → 可観測性ツールが認証情報ストアになってしまう → **秘密情報ではない
相関値のみをログに記録する。**
フォローアップの質問と回答
フォローアップ1:自己完結型のJWTではなくサーバーサイドセッションを使用するのはなぜですか?
要件であるグローバルな失効には、すでに最新のサーバー状態が必要です。不透明な識別子はクレームをブラウザから 排除し、1行の失効処理をシンプルにします。JWTも機能しますが、即時ログアウトには短い有効期限、失効リスト、 またはアカウントバージョンの検索が依然として必要です。署名単体ではこれを解決できません。
フォローアップ2:アイドル有効期限のためのリクエストごとの書き込みをどのように回避しますか?
信頼できる最終アクティビティ値を大まかなバケットで保持します(例:保存された値が数分前のものである場合にのみ更新する)。 導出されたアイドル期限が過ぎている場合、サーバーは依然として拒否します。バケットは、その最大延長幅が セキュリティポリシーに含まれるように選択し、その境界を明示的にテストします。
フォローアップ3:リージョン間ストアが利用できない場合はどうなりますか?
ルートのリスクを分離します。公開ルートや読み取り専用ルートは、ポリシーで許可されていれば制限付きのキャッシュされた決定を受け入れることができます。 管理者やその他の影響が大きいルートは、最新のエポック状態を取得するか、fail-closedにする必要があります。そうでなければ 5秒のグローバルログアウトの約束が偽りになるためです。これを可用性とセキュリティのSLOとして追跡します。
フォローアップ4:定期的なセッションIDの更新は常に有効にすべきですか?
いいえ。更新は盗まれた識別子の有効期間を短縮できますが、ハンドオフの競合を引き起こし、 アイドル有効期限や絶対有効期限の代わりにはなりません。まずはログイン時および権限変更時にローテーションします。定期的な更新は、 テスト済みのアトミックプロトコルと明確な脅威モデル上のメリットがある場合にのみ追加してください。
フォローアップ5:アクティブなデバイスをユーザーにどのように表示しますか?
作成日時、最近のアクティビティバケット、大まかなデバイスラベル、大まかな位置情報などの 秘密ではないメタデータを保存します。ユーザーが1行を失効させるか、全員のアカウントエポックをインクリメントできるようにします。ラベルはヒントであり、 デバイスの同一性の証明ではありません。生のセッション識別子がUIや監査エクスポートに含まれることは決してありません。
フォローアップ6:セッション固定化を最もよく検出できる単一のテストは何ですか?
認証前に選択された識別子から開始し、ログインを完了した後、別のクライアントからその古い値を使用して 保護されたリクエストを送信します。新しく発行された識別子が成功する一方で、古い値は必ず失敗しなければなりません。管理者昇格でも同様に繰り返し、 ストレージやログに置き換え後のベアラトークンが露出していないことを検証します。