Shikata Ga Nai

Private? There is no such things.

割引コードのバリデーションにおけるリミットオーバーランの競合状態(Race Condition)

Hello there, ('ω')ノ

💡 そもそも競合状態(Race Condition)とは?

競合状態とは、複数の処理が同時に走った場合に、プログラムが想定していない動作を引き起こすバグの一種です。特に、同じリソース(データベースの値など)に複数のリクエストが同時にアクセス・変更を行うと、チェックや更新のタイミング次第で不正な状態になります。


🛍 割引コードのバリデーションの脆弱性

たとえば、ユーザーが「割引コード」を使って支払い時に割引を受けられるとします。一般的にはこのような流れになります:

  1. サーバーは「この割引コードはすでに使用済みか?」を確認。
  2. 未使用であれば、割引を適用。
  3. データベースに「このコードはこのユーザーにより使用済み」と記録。

一見、これで問題ないように思えますが、重要なポイントは「ステップ2とステップ3の間にある短い時間」です。このわずかな「一時的な状態(sub-state)」の間に、ユーザーがほぼ同時に別のリクエストを送ったら?


⏱ 競合が発生する瞬間

この一時的な状態のことを「レースウィンドウ」と呼びます。このウィンドウの中で、同じユーザーが同じ割引コードを別リクエストで送れば、チェックはまだ未使用と判断され、何度も割引が適用されてしまうのです。


🔍 脆弱性を見つけ出すコツ

以下の手順で検証できます:

  1. ブラウザの開発者ツールを開く。
  2. 割引コードを送信するフォームを探す。
  3. 同じ割引コードを複数のタブやツール(Burp Suite など)から同時に送信
  4. 複数回割引が適用されれば、競合状態に成功

🛠 防ぐための対策

この問題を防ぐには以下の対策が必要です:

  • データベースレベルでのトランザクションロックの実装。
  • 処理の**アトミック性(不可分性)**の確保。
  • 割引適用処理に一意性制約を追加。

✅ まとめ

競合状態は、表面的には安全に見えるロジックにも潜む深刻な脆弱性です。今回の例のように「一時的な状態(sub-state)」に着目して、多重リクエストを工夫して送ることで、意図しない割引の複数適用などが可能になってしまいます。

👨‍💻 ペネトレーションテストでは、「ほんの一瞬のスキ」を狙う視点が重要!

割引コード以外にも、「1回限りのクーポン」「初回限定サービス」「一時的な権限付与」など、一時的に状態が変わる機能にはすべてこのリスクが潜んでいると考えて検証しましょう。

Best regards, (^^ゞ