代表的な面接トピック

一般面接:HTTP 508 Loop Detected が適切となるケースと、WebDAV トラバーサルサイクルの防止方法

一般普通
Offer.cc 編集チーム公開日 更新日

質問

WebDAV サービスが Depth infinity の PROPFIND 処理中に 508 を返します。セマンティクスを説明し、サイクル検出、レスポンス選択、クライアント互換性、DoS 制御を設計してください。

プロンプトと範囲

WebDAV サービスでは、コレクション間の複数のバインディングが許可されています。クライアントが Depth: infinity を指定して PROPFIND を送信し、サーバーがトラバーサルパス上に既に存在するコレクションに到達して 508 を返します。508 の正確な意味を説明し、一般的なリトライループと区別し、208 Already Reported が適切な場合を説明し、リソースの枯渇を防ぎ、クライアントの動作を定義してください。

これは、HTTP ステータスのセマンティクス、グラフトラバーサル、プロトコル互換性、セキュリティ境界を組み合わせた問題です。RFC 5842 では、ループに遭遇した後に無限深度の操作を終了するためのステータスとして 508 を定義しており、IANA は RFC 5842 に基づいて 508 を登録しています。

面接官がテストしていること

  • 508 が一般的なリダイレクト障害ではなく、WebDAV バインディングと Depth: infinity に関するものであることを理解しているか。
  • リソース、URI バインディング、パス、訪問済みコレクションをグラフとしてモデル化できるか。
  • 重複するリソースに対して 208 で処理を継続することと、クライアントが 208 をサポートしていない場合に 508 で操作全体を失敗させることを区別できるか。
  • 単に「訪問済みセット(visited set)を追加する」と言うだけでなく、決定論的な終了、バジェット、オブザーバビリティ、フォールバックを提供できるか。

最初に明確にすべき質問

  1. どのメソッドと Depth 値が使用されているか? セマンティクスは再帰的なトラバーサルに関係します。
  2. サーバーは RFC 5842 バインディングを実装し、DAV ケーパビリティをアドバタイズしているか? クライアントは 208 を解釈できるか?
  3. ノードの重複排除はリソース ID、正規 URI、バインディングパスのどれで行われるか? 1 つのリソースが複数の URI を持つ場合があります。
  4. レスポンスには部分的な結果を含めるべきか、それとも操作をアトミックに失敗させる必要があるか?
  5. 悪意のあるディープトラバーサルを抑制するために、ノード数、ボディサイズ、CPU 時間、認可のどのようなバジェットを設定するか?

30秒の回答フレームワーク

まず意味を限定します。508 は、サーバーがバインディングサイクルを検出した後に WebDAV の Depth: infinity 操作を終了したことを示します。一般的な HTTP リトライステータスではありません。バインディングをグラフとしてモデル化し、リソースの同一性によって後退エッジ(back edge)を検出します。クライアントが 208 を解釈できる場合は、リソースを一度報告して multistatus レスポンスを継続します。そうでない場合、508 で操作を明確に失敗させることができます。ノード、深さ、バイト数、時間のバジェットを適用し、サイクルを記録して、クライアントの自動リトライを停止させます。

ステップごとの回答

1. URL 文字列だけでなくリソースグラフをモデル化する

ノードはリソースまたはコレクションであり、エッジはバインディングです。URI はアクセスパスであり、必ずしもリソースの同一性を示すものではありません。1 つのリソースが複数のバインディングを持つことがあります。重複排除、監査、診断のために、リソース ID、現在のパス、親パスを保持します。

2. 明示的な DFS または BFS 状態を使用する

アクティブな再帰スタックと報告済みセット(reported set)を維持します。ノードに入る前に、それがアクティブなスタックに既に存在するかどうかを確認します。それが後退エッジです。別のパスを介して同じリソースに到達した場合は、無限に展開するのではなく、プロトコルケーパビリティに従って既に報告済みとして処理できます。

text
visit(node, path):
  if node in activePath: return CYCLE
  if node in reported: return ALREADY_REPORTED
  budget.consume(node)
  activePath.add(node)
  report(node)
  for child in children(node): visit(child, path + child)
  activePath.remove(node)

activePath は実際のサイクルを識別し、reported は複数のバインディングによる重複出力を防止します。1 つのセットで両方のケースを安全に表現することはできません。

3. クライアントのケーパビリティから 208 または 508 を選択する

クライアントがバインディング拡張と 208 のサポートをアドバタイズしている場合、サーバーは最初の出現を通常どおり返し、以降のバインディングを multistatus レスポンス内で Already Reported としてマークし、その子孫を省略できます。クライアントが 208 を理解しない場合、RFC 5842 は無限深度の操作が 508 で失敗する互換パスを提供します。失敗を 200 で偽装してはなりません。

4. 決定論的なバジェットとセキュリティ境界を設定する

非巡回グラフであっても、CPU、メモリ、またはレスポンススペースを枯渇させる可能性があります。最大ノード数、アクティブパス長、合計バイト数、実時間(wall-clock time)、並行性を設定します。バジェットが枯渇した場合は、理由を記録し、リトライするのではなく明確に許容された失敗を返します。マルチステータスレスポンスを通じて隠蔽されたノードが漏洩しないように、テナントバインディング全体で認可を確認します。

5. 書き込みと並行するトポロジ変更を処理する

BIND、REBIND、UNBIND はグラフを変更します。操作の途中でトポロジ変更によって検出が無効化されないように、一貫したスナップショットまたはバージョンをトラバースします。巡回する可能性のあるバインディングを作成する前に、到達可能性チェックを実行するか、明示的なサイクル許可の事前条件を要求します。チェックとコミットは 1 つのトランザクション境界内に保持します。

6. クライアントによる有害なリトライを停止させる

508 は要求された操作が失敗したことを意味します。クライアントはこれを 503 のように扱ってバックオフを伴う盲目的なリトライを行うべきではありません。ボディと相関 ID を読み取り、深度を減らすか、バインディングを修復するか、ケーパビリティを問い合わせます。508 を一般的なエラーにマッピングするプロキシは、元のステータスと診断フィールドを保持する必要があります。

質の高い模範回答

バインディングを有向グラフとしてモデル化し、リソースの同一性とアクセス URI を区別します。Depth: infinity の場合、アクティブな再帰スタックを使用して後退エッジを検出し、複数のバインディングが同じリソースに到達したときの重複出力を抑制するために報告済みセットを使用します。これらのセットは異なる意味を持ちます。ノード数、深さ、レスポンスバイト数、時間のバジェットを設けて、一貫したビューをトラバースします。

クライアントが RFC 5842 および 208 サポートをアドバタイズしている場合は、207 multistatus レスポンスで最初のリソースを返し、以降の重複を Already Reported としてマークし、その子孫を省略します。208 を理解しないクライアントに対しては、互換パスで指定されているとおり、無限深度操作全体を 508 で終了します。508 は一般的なリトライエラーではないため、クライアントは自動リトライを停止し、グラフを修復するか深度を下げる必要があります。サイクルやディープトラバーサルが DoS ベクターにならないよう、認可、メトリクス、相関ログを適用します。

よくある失敗パターン

  • 508 をリバースプロキシのリトライ枯渇や一般的な URL リダイレクトループと呼ぶこと。
  • URI 文字列のみで重複排除を行い、1 つのリソースへの複数のバインディングを見落とすこと。
  • グローバルな訪問済みセットのみを使用し、共有リソースと後退エッジを混同したり、208/508 の区別を失ったりすること。
  • ノード、バイト数、時間、認可のバジェットなしで Depth: infinity を受け入れること。
  • 508 に対して自動的にリトライし、同じトポロジに対して繰り返しリソースを消費すること。
  • ケーパビリティネゴシエーション、207 multistatus レスポンス、または書き込み競合を忘れること。

フォローアップの質問と模範回答

508 と 208 の境界は何ですか?

208 は、クライアントがバインディング拡張を理解している場合に、以前に確認されたリソースを報告し、操作の残りを継続できるようにします。508 は、サイクルが発生した後に無限深度の操作全体を終了するもので、通常は 208 を使用できないクライアント向けです。

訪問済みキーとして URI のみを使用しないのはなぜですか?

複数の URI が 1 つのリソースにバインドできるため、URI のみの追跡ではトラバーサルが繰り返されます。診断にはパスも重要であるため、リソースの同一性とパスコンテキストの両方を保持します。

チェックと書き込みの間の TOCTOU をどのように回避しますか?

到達可能性の検証とバインディングのコミットを 1 つのトランザクションまたはバージョン管理されたスナップショット内で実行します。アトミック性がノード間にまたがる場合は、バージョン条件で失敗させ、再チェックします。

クライアントは 508 の後にリトライすべきですか?

盲目的にリトライすべきではありません。現在のトポロジによって操作が失敗したため、深さを減らすか、サイクルを修復するか、ケーパビリティ情報を取得する必要があります。トポロジが意図的に変更された後にのみリトライします。

偽陽性のサイクル検出をどのようにテストしますか?

非巡回 DAG、共有リソース、バインディングサイクル、および深度制限用のフィクスチャを使用します。結果数、ステータスコード、トラバーサルバジェット、監査ログ、レスポンスサイズ制限をアサートします。

508 はすべてのマイクロサービス呼び出しサイクルに適用されますか?

いいえ。その標準化された意味は WebDAV RFC 5842 に由来します。その他のサービスループには独自の契約エラーが必要です。単に 5xx を維持しても、レスポンスが WebDAV 508 のセマンティクスを持つことにはなりません。

公開情報ソース

関連する質問