Shikata Ga Nai

Private? There is no such things.

第35回:実例で学ぶビジネスロジックの脆弱性

Hello there, ('ω')ノ

1. ビジネスロジック脆弱性とは?

アプリが「正しく」動いた結果、ビジネスルールを破壊してしまう状態 (入力検証など技術的な脆弱性ではなく、仕様上の抜け穴)

  • コードはエラーを出さず、ステータス 200 を返す
  • WAF/スキャナでは検出困難
  • 被害は 売上損失・ポイント濫用・在庫崩壊 など経営直撃系

2. 代表的な実例3選(※実話をベースに構成を変更)

事例 説明 インパクト
① クーポン二重適用 「新規10%OFF」→「送料無料」→さらに「総額10%OFF」
UIは順番制限想定も、API 直接叩きで重複成功
1週間で3,000万円相当の値引き被害
② ネガティブ残高決済 ギフト券を 0 円以下に設定→返金処理でマイナス残高を現金化 ハンター1名で 100万円引き出し→報告&修正
③ 引き継ぎフローの競合 電話番号変更→SMS認証→旧番号に戻す…を高速ループ
→ ワンタイムコードを大量取得、OTP枯渇で他ユーザがログイン不可
サービス丸一日停止+ユーザ離脱

3. どこが問題だったのか?

① クーポン二重適用

  • UI では排他制御API では順番チェックが無い
  • “クーポン適用”→“カート再計算”の並列呼び出し競合

② ネガティブ残高決済

  • 金額フィールドに 負数を許容(会計上“貸方”想定)
  • 返金ロジックが 絶対値 で処理 → マイナスをプラスで返却

③ 引き継ぎフロー競合

  • 独立したマイクロサービスA(SMS送信)とB(番号更新)
  • 分散トランザクションのロックが無く、同一ユーザで無限ループ可

4. バグハンターのテストポイント

観点 試すこと ツール例
並列実行 同一 API を 2リクエスト同時送信 Burp Intruder(Thread=2)
順番入れ替え 「Step1→Step2」を「Step2→Step1」で呼ぶ Postman Collection Runner
極端値 0, -1, 99999999 curl/JQ
レースコンディション 在庫=1 の商品を 2タブ同時購入 Selenium+マルチスレッド

5. 報告時の“説得力UP”要素

  1. 攻撃シナリオを短く可視化
  2. 損害試算

    • 「1回ループで○円得」×「Bot で1日○回」=○○円
  3. 再現 PoC
   #!/bin/bash
   for i in {1..2}; do
     curl -X POST https://api/shop/applyCoupon -d 'code=NEW10' -H "Auth: user"
   done
  1. 修正提案

    • クーポンテーブルに unique_user カラム追加
    • 在庫をDBレベルで CHECK (qty>=0) 制約

6. 開発側へのチェックリスト

カテゴリ チェック項目
取引の原子性 カート更新・決済を DBトランザクション で包む
状態遷移 有効→無効→有効…とループできないか
金額・数量バリデーション 負数・小数 を禁止、範囲制限
並列制御 行ロック / Redis 分散ロック / 業務IDempotency
監視 値引き総額・返金頻度に アラート閾値

7. まとめ:仕組みは正しく、ビジネスが壊れる

  • 技術バグが無くても “想定外フロー” で損害は発生
  • バグハンターは 「ユーザが得する裏技」 を探す視点が鍵
  • 開発側は トランザクション設計+メトリクス監視 で早期検知

“ルールの隙”はツールでは拾えない。 だからこそ人間のクリエイティブなテストが価値になる。

Best regards, (^^ゞ