Race Condition(競合状態)
Race Conditionとは、Webアプリケーションに対して、同時に同データを送信することによって、意図しない処理を引き起こす脆弱性です。
Race Conditionの悪用の主な問題は、リクエストが非常に短い時間差(通常>1ms)にて並行して処理される必要があります。
Race Conditionの種類
Limit overrun race conditions
特定のリソースへの同時アクセスの上限を超えた場合、競合が発生する可能性があります。
例として、複数のプロセスが同時にファイルに書き込みを行い、ファイルのサイズが制約よりも大きくなることです。
Hidden multi-step sequences
複数のステップから構成される操作によって、競合状態を引き起こします。
競合状態によって、処理によってスキップされたり、逆順に実行されたりする可能性があります。
予約と支払いが複数のステップで行われ、同時に別の予約を行った場合に支払いが競合する。
Methodology
複数のステップから隠れたシーケンスをを見つけて活用するために、PortSwigger Researchのホワイトペーパー「 Smashing the state machine: The true potential of web race conditions」から要約した次の方法をお勧めします。

- 衝突の可能性を予測する全てのエンドポイントをテストするのは非現実的です。ターゲットのWebサイトを通常通りに調査した後、以下の内容を自問することでテストすべきエンドポイントを絞り込めます。
- このエンドポイントはセキュリティ上重要か?多くのエンドポイントは重要な機能に関わらないため、テストする必要はありません。
- 衝突の可能性はあるか?衝突させるには、同じデータを対して同時に操作を行う複数のリクエストが必要です。例えば、パスワードリセットの異なる実装例を見てみましょう。
- 最初の例では、異なる2人のユーザーに対して同時にパスワードリセットをリクエストしても、別々のデータが変更されるため、衝突は起きにくいです。しかし、2つ目の実装では、異なるユーザーのリクエストでも同じデータを変更できるため、衝突の可能性があります。
- 手がかりを探す手がかりを探すには、まずエンドポイントが通常の条件下でどのように動作するかを確認します。Burp Repeaterでリクエストをグループ化し、「Send group in sequence (separate connections)」オプションを使います。詳細については、「Sending requests in sequence」を確認してください。次に、ネットワークの影響を最小限に抑えるために、同じリクエストグループを一度に送信します。これは、single-packet attack(or last-byte sync if HTTP/2 isn’t supported)を使用して、同じリクエストグループを一度に送信します。Burp Repeaterでこれを行うには、「Sending requests in parallel」を参照してください。または、BApp Storeから入手できるTurbo Intruder拡張機能を使用することもできます。どんな変化も手がかりになることは可能です。ベンチマーク時に確認した結果からのどんな小さな変化も見逃さないよう注意してください。レスポンスの変化やメールの内容が変わったり、その後アプリの動作に変化が現れるといった二次的な影響も考慮しましょう。
- 動作を確認する何が起こっているのかを理解し、不要なリクエストを取り除いても同じ効果を再現できることを確認しましょう。高度なRace Conditionは、通常とは異なる独特の動作を引き起こすことがあります。そのため、最大の影響を引き出す方法がすぐにはわからない場合もあります。各Race Conditionを個別の脆弱性としてではなく、全体の構造的な弱点として捉えることが役立つかもしれません。
Multi-endpoint race conditions
複数のエンドポイントから同時にリソースにアクセスした場合、競合が発生します。複数のAPIエンドポイントが同時にデータベースに書き込みによって競合する。
Single-endpoint race conditions
単一のアクセスポイントが同時にリソースにアクセスし、競合が発生する。オンラインでの商品の在庫管理で、同時に複数のユーザーが同じ商品を購入しようとした場合、複数の操作が競合してデータ破損やセキュリティ上の問題が発生する。
Session-based locking mechanisms
セッション単位でロック機能が不十分で、同時に複数のセッションに競合が発生します。複数のユーザが同時にセッションデータを更新するとロックの競合が発生する。
Partial construction race condition
オブジェクトやデータ構造のがまだ完全に構築されていない状態で一部を同時に操作された場合、競合が発生します。同時にオブジェクトやデータ構造が構築中にアクセスされ、不完全な状態で処理が行われる可能性があります。
Time-sensitive attacks
特定なタイミングにリクエストを送信することによって、競合状態が発生します。タイムアウトのある認証メカニズムで、重要な操作(パスワードリセットなど)を行うことによって、セキュリティ上の問題を引き起こす可能性があります。
対策
- 異なるデータストレージ間二手データを混在させないようにする。
- 重要なエンドポイントの変更を一回で確実に完了するようにデータストアの同時実行機能を使用します。例えば、支払いがカートの金額と一致することを確認し、注文を確定するために、1つのデータベーストランザクションを使用します。
- 多層防御を強化するために、列の一意性制約などのデータストアとの整合性と一貫性の機能を活用します。
- 1つのデータストレージ層を使って、別の層を保護しよう注意してください。例として、セッションはデータベースのリミット超過攻撃を防ぐためには適していません。
- セッション管理フレームワークがセッションを内部的に一貫性を保つようにします。セッション変数を一括でなく個別に更新するのは、一見効率的に思えるかもしれませんが、非常に危険です。このことをORM(オブジェクト関係マッピング)にも当てはまります。トランザクションのような概念を隠すことで、それらの管理責任を完全に担うことになります。
- 一部のアーキテクチャでは、サーバ側の状態管理を安全に避けることが適切な場合もあります。代わりにJWT(JSON Webトークン)などを使って暗号化し、状態をクライアント側に持たせることが考えられます。ただし、これには独自のリスクが伴うため、JWT攻撃に関するトピックで詳しく説明しています。